CHANGE ?= slice-0-skeleton указывал на давно заархивированный change, make check был красным (O-1 плана 0, #116). По умолчанию теперь openspec validate --all --strict; CHANGE=<id> — один change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
27 KiB
Проверки качества: цели make и порядок перед PR
Конфигураций CI в репозиториях нет (в Gitea CI не используется, пайплайн платформы банка вне зоны проекта), поэтому
проверки выполняются локально одной командой перед каждым PR. Требования — spec quality-checks
(openspec/specs/quality-checks/spec.md), решение D-8 (openspec/changes/archive/2026-10-04-slice-0-skeleton/design.md).
Все цели запускаются из корня репозитория сервиса (arm-core, tabular-parser, scan-gateway); рецепты сами
переходят в api/ и используют окружение api/.venv. Инфраструктура compose — docs/dev/docker-compose.yml
(см. README.md); в workspace и в worktree .wt/<срез>/ она лежит рядом как ../docs/dev/.
Общие цели
Одинаковый набор целей во всех трёх сервисных репозиториях; семантика, описанная ниже, — эталонная (реализации расходятся в деталях, см. «Известные расхождения реализаций»). Репозиторий вправе добавлять свои цели (см. ниже), но не меняет смысл общих.
| Цель | Что делает | Нужен Docker / compose |
|---|---|---|
make venv |
api/.venv на Python 3.11 через uv; ставит requirements.txt без строк raisa_* (пакеты Nexus банка вне банка недоступны, D-10) и requirements-dev.txt |
нет |
make lint |
ruff check ., ruff format --check ., mypy src (в arm-core — mypy src scripts) |
нет |
make test |
pytest -m "not integration": юнит-тесты; включает тест идентичности вендоренного контракта api/contracts/ с ../docs/contracts |
нет |
make test-integration |
pytest -m integration против инфраструктуры compose (PostgreSQL 16, NATS, MinIO; бакет arm-ka должен существовать, его создаёт make up) |
да, make up |
make check |
make lint затем make test; останавливается на первой упавшей проверке (только при последовательном запуске: lint и test — пререквизиты, при make -j они идут параллельно) |
нет |
make check-full |
make check + make test-integration + сборка docker-образа (docker build) + statements-check (свежесть копии общего кода arm_statements, см. ниже); для arm-core — расширенный набор, см. ниже |
да, make up и Docker |
make up |
поднимает инфраструктуру из ../docs/dev/docker-compose.yml; постоянные сервисы postgres, nats, minio |
Docker |
Сводка шагов make check: ruff check . → ruff format --check . → mypy src (в arm-core — mypy src scripts) →
pytest -m "not integration".
Любой ненулевой код останавливает цель на этом шаге, следующие не выполняются. Запускайте без -j: при параллельной сборке шаги идут одновременно. make check не требует Docker,
PostgreSQL, NATS и сети.
make up
Поднимает compose-проект arm-ka (имя зафиксировано в compose, поэтому тома общие для любого worktree):
docker compose -f ../docs/dev/docker-compose.yml --project-name arm-ka up -d --wait postgres nats minio
docker compose -f ../docs/dev/docker-compose.yml --project-name arm-ka run --rm minio-init # бакет arm-ka (идемпотентно)
Почему так: голый docker compose up -d --wait возвращает код 1 (одноразовый minio-init завершается кодом 0, compose
считает это сбоем), поэтому --wait даётся со списком долгоживущих сервисов, а бакет создаётся отдельным шагом. Без
бакета интеграционные тесты на свежем томе падают на NoSuchBucket. Целей down/stop/restart нет намеренно: проект
arm-ka общий для всех сервисов; остановка и сброс — вручную (docker compose --profile services down, при сбросе данных down -v с тем же --profile services,
иначе контейнеры сервисов останутся), см. README.md.
Эталонная реализация
make up— вscan-gateway. Реализации вarm-coreиtabular-parserотличаются (см. «Известные расхождения реализаций»).
Состав make check-full по репозиториям
| Репозиторий | Шаги по порядку |
|---|---|
scan-gateway |
check → statements-check → test-integration → docker-build (образ scan-gateway:dev); интеграционный тест поднимает эмулятор ИС Сканирование в процессе теста |
tabular-parser |
check → statements-check → test-integration → docker-build (образ tabular-parser) |
arm-core |
check → statements-check → dbml-check → check-infra → test-integration → docker-build → web-check |
statements-check появляется в сервисе вместе с копией пакета (PR подключения хоста); до этого цели нет.
Копия общего кода arm_statements и sync-statements
Общий код нормализации ББ/ОФР (05-domain-rules.md §11) живёт в репозитории arm-statements пакетом arm_statements/;
в arm-core, tabular-parser и scan-gateway он лежит копией — прямые правки копии запрещены (Р-1, design
openspec/changes/unified-statement-normalization/design.md). Скрипт и цели docs (make -C docs sync-statements,
statements-check, test-sync-statements) — arm-ka-docs#120; statements-check сервисов появляется с PR подключения
копии в каждом хосте (план 2, задачи 7–9) — до шага 3 выкладки. Раздел описывает поведение по спеке
statement-normalization (требование «sync-statements…»).
- Где копия:
api/arm_statements/(рядом сapi/src/; импортimport arm_statements—api/вsys.path, как дляsrc). Пакет приносит свои тесты (api/arm_statements/tests/); конфиг линтеров пакета в копию не кладётся — хост держитarm_statementsв строгом блокеmypyи вtestpaths(P2-1, P2-3);ruff,pytestиmypyхоста охватывают копию:ruffиpytest— по каталогуapi/(тесты пакета подключены какtestpaths),mypy—MYPY_PATHSхоста дополняетсяarm_statements(mypy src arm_statements, вarm-core—mypy src scripts arm_statements). - Маркер версии:
api/arm_statements/VERSION— полный хэш коммитаarm-statements, из которого разложена копия; пишет только скрипт. Сервис при старте читает его и пишет в результатservice.statements_version; ядро сравнивает его сstatements_versionзадания при пересчёте после правки (STATEMENTS_VERSION_MISMATCH,info). - Скрипт:
docs/scripts/sync-statements.sh(по образцуsync-web.sh), запуск из workspace илиmake -C docs sync-statements SYNC_ARGS=…. Источник — локальный checkoutarm-statementsрядом (../arm-statements) или--source <dir>, ссылка —origin/mainили--ref <ref>; хост —--host arm-core|tabular-parser|scan-gatewayили--host-dir <dir>. Без--check— раскладывает копию нужного коммита в новый worktree хоста на ветке фичи и пишетVERSION; issue и PR — как уsync-web.sh;--dry-run— то же без issue, проверок, коммита и PR. С--in-place— раскладывает копию в рабочее дерево существующего worktree хоста без git-операций (подключение хоста; PR фичи, которой нужна новая версия пакета; при прямой правке копии или недостающей зависимости — отказ20, ничего не пишется). Обзор по трём хостам рядом —make -C docs statements-check; сборка снимка для контура (prepare-contour-snapshot.sh) вызывает--checkдля хостов, где копия уже есть (Р-42). --check, коды возврата (файлVERSIONв сравнении не участвует — в источнике его нет):0— копия равна деревуarm_statements/коммита изVERSION, это коммит--ref, и копия перечня кодов равна docs;10— копия равна дереву коммита изVERSION, но в--refесть более новый (копия отстала), или байтовая копия перечня кодовarm_statements/rules/statement_code_orders.yamlотличается отdocs/contracts/statement-code-orders.yaml(перечень в docs правился раньшеarm-statementsили пересинхронизации хоста) — предупреждение, не ошибка;20— копия отличается от дерева коммита изVERSION(прямая правка),VERSIONнет в истории источника, зависимости пакета (списокdependenciesегоpyproject) отсутствуют в зависимостях хоста, либо (толькоarm-core) код словаря имёнapi/rules/statement_lines.yamlотсутствует в перечне своей редакции копии пакета;2— нет checkoutarm-statementsили хоста, неизвестный аргумент. Файлы расхождения перечисляются в выводе. Порядок правки перечня кодов: PR в docs → PR вarm-statements→sync-statementsв хостах («сначала ядро»); до конца цепочки хосты получают10.- Конфиг линтеров и тесты пакета: пакет проходит самый строгий конфиг
ruff/mypyсреди хостов (в своём репозитории), потому что конфиг хоста применяется к копии, а править копию нельзя; тесты пакета — со своимconftest.py,autouse-фикстурыconftestхоста ограничены каталогомtests/хоста. statements-checkвmake check-full:../docs/scripts/sync-statements.sh --check --host-dir .(docs —../docsилиARM_KA_DOCS); код0и10— цель зелёная (при10предупреждение печатается, в описание PR вписывается, что копия отстала),20и2— цель красная. Вmake checkи в пайплайн платформы не входит: тест, читающий соседний репозиторий, уже ломалmain(arm-core #33).
Для arm-core дополнительно:
dbml-check—python scripts/check_dbml_vs_orm.py ../../docs/model/data-model-v5.19.dbml(путь отapi/, по умолчанию — переменнаяDBMLMakefile, она указывает на текущую версию модели; другой —make dbml-check DBML=<путь относительно api/>): ORM обязана совпадать с DBML; расхождения печатаются построчно, код 1.- Цикл миграций «
alembic upgrade head→alembic downgrade base→alembic upgrade head» на чистой БД, пустой autogenerate и сверка схемы сcreate_allORM выполняетtests/test_migrations.py; он входит вtest-integration, поэтому отдельным шагом вcheck-fullне гоняется. Для быстрой проверки одних миграций:make migration-cycle. check-infra— TCP-проверка доступности PostgreSQL и NATS изapi/.env.test; вместо длинного трейсбека pytest подсказывает «поднимите инфраструктуру:make up».web-check—npm install,biome check src,tsc --noEmit,npm run buildвweb/.
Дополнительные цели репозиториев (не общие): arm-core — migrate, seed, seed-dev, dbml-check,
migration-cycle, check-infra, docker-build, web-check; scan-gateway — help, docker-build, emulator;
tabular-parser — docker-build.
Эталонный фрагмент dev/Makefile.common
Рецепты общих целей вынесены в Makefile.common — это эталон (источник — Makefile scan-gateway),
с которым сервис сверяется diffом или который подключает напрямую:
# Makefile сервиса: свои переменные и цели; цель по умолчанию задана явно, иначе ею станет первая цель include (venv)
.DEFAULT_GOAL := check
MYPY_PATHS := src scripts # arm-core; по умолчанию src
CHECK_FULL_STEPS := check dbml-check check-infra test-integration docker-build web-check # arm-core
include ../docs/dev/Makefile.common
Переменные (все ?=): VENV, BIN, MYPY_PATHS, COMPOSE_FILE, COMPOSE_PROJECT, IMAGE (по умолчанию имя каталога
сервиса; у scan-gateway — scan-gateway:dev), CHECK_FULL_STEPS (по умолчанию check test-integration docker-build).
Определяет цели venv, lint, test, test-integration, check, docker-build, check-full, up.
Цели, специфичные для сервиса (migrate, seed, dbml-check, check-infra, web-check, emulator, help),
остаются в его Makefile.
Проверка «make check вызывает одинаковые команды»: make -n <цель> в каждом сервисе и в Makefile, подключающем
Makefile.common с теми же переменными, даёт одинаковый вывод. Текущее состояние (Срез 0, сервисы не менялись):
scan-gateway совпадает с эталоном полностью; arm-core и tabular-parser — по venv, lint, test,
test-integration, check, docker-build и составу команд check-full, но не по up и по структуре check-full
(пререквизиты вместо вызовов $(MAKE)); см. «Известные расхождения реализаций». Сервисы переходят на include в
следующих срезах (решение 24 в design.md).
Внешние ресурсы: docs и fixtures опциональны
Проверки сервисов обращаются к соседним репозиториям workspace и не должны падать, если их нет: тест
пропускается (skipped) с явной причиной, остальные выполняются, make check остаётся зелёным.
| Что | Где ищется | Что пропускается без него |
|---|---|---|
docs |
../docs или каталог из ARM_KA_DOCS |
тест идентичности контракта api/contracts/ с docs/contracts/; в arm-core ещё сверка с DBML в тестах (dbml-check как цель make требует DBML и падает без него) |
fixtures |
../fixtures или каталог из ARM_KA_FIXTURES (в arm-core; в остальных сервисах — см. «Известные расхождения реализаций») |
тесты с маркером fixtures (реальные данные, внутренний доступ): причина пропуска — «ARM_KA_FIXTURES недоступен …» (в arm-core) |
Пример (docs и fixtures лежат не рядом):
export ARM_KA_DOCS=/path/to/arm-ka-docs
export ARM_KA_FIXTURES=/path/to/arm-ka-fixtures
make check
Причины пропусков видно в выводе (pytest --verbose в addopts: строки SKIPPED (…)); полный список —
cd api && .venv/bin/pytest -m "not integration" -rs.
Важно: зелёный make check с пропусками не доказывает идентичность контракта и прохождение на реальных данных. Перед
PR убедитесь, что docs и fixtures доступны (пропусков по этим причинам нет) либо явно укажите в описании PR, что
пропущено.
Фактическая поддержка переменных различается по репозиториям — см. «Известные расхождения реализаций».
Проверки этого репозитория (docs)
В репозитории arm-ka-docs нет сервисных целей; Makefile в его корне содержит только проверки спеки и контракта:
| Цель | Что делает |
|---|---|
make venv |
.venv (Python 3.11, uv) с jsonschema и PyYAML — зависимостями скриптов (после обновления репозитория — перезапустить) |
make validate-contracts |
python scripts/validate_contracts.py: примеры contracts/examples/ против текущих схем JSON Schema 2020-12, примеры contracts/examples/v1/ — против схем прежнего MAJOR *.v1.schema.json (окно перехода на контракт 2.0), форма упорядоченных перечней кодов contracts/statement-code-orders.yaml (коды — 4 цифры формы; в варианте full код стоит 1 + (вхождений в repeats) раз, в simplified — не чаще); код 1 при любой ошибке |
make validate-openspec |
openspec validate --all --strict — все спеки и незаархивированные change'и (один change: make validate-openspec CHANGE=<id>) |
make check |
все проверки выше и test-snapshot, test-sync-web, test-sync-statements |
make test-sync-statements |
сценарии sync-statements.sh на синтетических репозиториях: коды --check, --in-place, --dry-run, отказ при прямой правке |
make sync-statements |
копия пакета arm_statements в хост одной командой (worktree, issue, PR); SYNC_ARGS="--host arm-core [--check|--dry-run]" |
make statements-check |
обзор свежести копий по трём хостам рядом (arm-core, tabular-parser, scan-gateway): строка итога на хост; в check не входит |
Без make: python scripts/validate_contracts.py (нужны jsonschema и PyYAML) и openspec validate <id> --strict.
Менять contracts/** нужно осознанно: байтовые копии схем лежат в api/contracts/ каждого сервиса, после правки их
нужно пересинхронизировать (cp), иначе тест идентичности в сервисах упадёт. Правило теста идентичности (у ядра и
сервисов одно, верно до, во время и после окна перехода между MAJOR — сейчас 1 → 2): схема копии с $id …/v<M>
сверяется с <имя>.v<M>.schema.json, если такой файл есть в docs, иначе с основным (у recognition-request файла
.v1 нет — копия v1.0 сверяется с основным); пример examples/<имя>.json копии — с examples/v<M>/<имя>.json, если он
есть, иначе с examples/<имя>.json (M — MAJOR копии схемы этого вида, вид — по префиксу имени); прочие файлы копии
(*.v<N>.schema.json, examples/v<N>/ у ядра) — с одноимёнными; файлы docs другого MAJOR сервис не сверяет. Правило
вводится во все три репозитория до слияния контракта v2.0 в docs (шаг 0 выкладки; спека recognition-contract,
требование «Копия контракта в сервисе идентична источнику»). Ядро держит копии обоих MAJOR. Правило реализовано файлом api/tests/contract_identity.py, побайтно одинаковым в трёх репозиториях; правка — сразу во всех трёх. Перечень кодов contracts/statement-code-orders.yaml
копируется не в сервисы, а в пакет arm_statements (сверяет sync-statements --check).
Порядок перед PR
- Поднять инфраструктуру:
make up(в репозитории сервиса). Убедиться, чтоdocsиfixturesдоступны (рядом или черезARM_KA_DOCS,ARM_KA_FIXTURES— последняя поддерживается только вarm-core, см. «Известные расхождения реализаций»). make check— быстрые проверки. Исправлять до зелёного.make check-full— полная проверка (интеграционные тесты, образ; дляarm-core— ещё DBML, миграции,web).- Если менялись
contracts/,model/или спека в репозиторииdocs: в нёмmake check, затем в каждом затронутом сервисе — пересинхронизация копий контракта и пункты 2–3. - Итог
make check-full(зелёный/красный, число тестов, что пропущено и почему) вписать в описание PR.
Правила: PR создаётся только после зелёного make check-full; один OpenSpec change и один PR — на один репозиторий;
make check-full не заменяет ревью.
Типичные проблемы
make test-integrationпадает на соединении — не поднята инфраструктура:make up; порты заняты — поменять вdocs/dev/.envи вapi/.env.test/api/.envсервиса (README.md).NoSuchBucket— бакета нет на свежем томе:docker compose -f ../docs/dev/docker-compose.yml --project-name arm-ka run --rm minio-init.tabular-parser: интеграционный тест получает чужие ответы — на шине compose не должно работать другого экземпляра сервиса из той же queue group (интеграционный тест поднимает сервис в процессе теста).- Нет
uv/Python 3.11 —make venvих требует (версия Python — как на платформе). docker build(иnpm installвweb-checkуarm-core) падает без сети — это шагиcheck-full, которым нужна сеть (образы, пакеты);make checkсети не требует.
Известные расхождения реализаций
Фактические реализации в Makefile трёх сервисов расходятся с описанной выше семантикой. Сервисы в рамках Среза 0
не менялись; расхождения занесены как находки ревью.
make up. Эталон —scan-gateway(--project-name arm-ka,--waitсо списком сервисов, затемrun --rm minio-init).tabular-parserне создаёт бакет (нет шагаminio-init) и на свежем томе даётNoSuchBucket;arm-coreвыполняетdocker compose up -dбез--wait, то есть возвращается до готовности сервисов.- Структура
check-full. Вscan-gatewayшаги вызываются через$(MAKE)(порядок и остановка на первой ошибке гарантированы и приmake -j); вarm-coreиtabular-parser— пререквизитами (при последовательном запуске эквивалентно, приmake -jпорядок не гарантирован). Запускайте без-j. ARM_KA_DOCS/ARM_KA_FIXTURES.arm-coreучитывает обе переменные;tabular-parser— толькоARM_KA_DOCS(тестов с маркеромfixturesпока нет);scan-gatewayучитываетARM_KA_DOCSв тесте идентичности контракта (с шага 0 #116), аARM_KA_FIXTURES— нет (ищет../fixtures/ocrпо фиксированному пути; тексты причин пропуска не содержат «ARM_KA_FIXTURES недоступен»). Пока расхождение по fixtures не устранено, дляscan-gatewayfixtures нужно класть рядом.