arm-ka-docs/dev/contour-transfer-guide.md

22 KiB
Raw Blame History

Перенос сервисов АРМ КА в контур банка: гайд для разработчика

Разработка ведётся снаружи (workspace awp-ca, Gitea проекта). В контур банка (GitLab платформы РАИСА и его CI/CD; Gitea в контуре нет) уезжают только три сервисных репозитория — arm-core, tabular-parser, scan-gateway — снимками дерева, без истории. Репозитории docs и fixtures в контур не переносятся: решение владельца 06.10.2026. Этот документ — что именно уезжает, чего в платформе нет, как подготовить снимок скриптом и как его положить в контур.

Соглашения платформы, от которых всё отталкивается, — context/03-architecture.md, разделы «Соглашения шаблона команды» и «Что ожидаем от платформы». Проверки качества — dev/checks.md.

1. Что переносится и что нет

Что В контур Почему
api/ (код, тесты, contracts/, rules/, alembic/, scripts/), web/ (arm-core), README.md, Makefile, setup/, raisa_config.yml, api/.env.example, api/.env.test да это и есть сервис по шаблону платформы
openspec/specs/, openspec/changes/archive/ да спецификация системы и журнал решений (design.md); без них код в контуре нечем объяснять
.claude/, CLAUDE.md, openspec/config.yaml нет инструкции и контекст AI-агента (в config.yaml — блоки context/rules с путями ../docs, ../fixtures), инструментарий внешней разработки; openspec CLI в контуре нет, specs/ и design.md читаются без него
docs/superpowers/ (планы, ledger) нет рабочие артефакты процесса, содержат локальные пути разработчика
Dockerfile, .dockerignore нет образ для локального make check-full; в контуре образ собирает платформа своим CI (шаблон держит Dockerfile в .gitignore)
История коммитов нет снимок одним коммитом; в истории снаружи — трейлеры Co-Authored-By: Claude …, Claude-Session, ссылки на внешний Gitea
Репозиторий docs нет контекст, модель, контракты, экспорт ТЗ — остаются снаружи; копия контракта лежит в api/contracts/ каждого сервиса
Репозиторий fixtures нет реальная отчётность для регрессии парсера; тесты с маркером fixtures в контуре пропускаются
reference-old-arm нет старый репозиторий банка, читался только для справки

Снимок получает файл SNAPSHOT.md с хешем коммита-источника и датой: по нему потом сверяется, что уже перенесено.

2. Чего в платформе нет и что есть

Нет в контуре (всё это скрипт вырезает или заменяет):

  • uv — make venv снаружи ставит окружение через uv venv --python 3.11 и uv pip. В снимке цель venv переписана на python3.11 -m venv + pip с setup/pip.conf (Nexus), как делает setup/setup.sh шаблона.
  • Docker и docker compose — локальная инфраструктура (docs/dev/docker-compose.yml) живёт в docs, которого в контуре нет. Цели make up, test-integration, check-full в контуре не запускаются; docker-build удалена из Makefile.
  • docs и fixtures рядом с сервисом — тесты идентичности контракта и тесты на реальных данных пропускаются (раздел 5), сверка ORM с DBML (make dbml-check в arm-core) не выполняется.
  • tea, codegraph, openspec (CLI), хуки .claude/hooks — инструментарий процесса; его конфигурация (.claude/, CLAUDE.md, openspec/config.yaml) вырезается.
  • Внешний Gitea git.aepif.ru — ссылки вида https://git.aepif.ru/RSHB/arm-core/pulls/19 в архивах change'ей скрипт превращает в RSHB/arm-core#19 (номера относятся к внешнему Gitea, в контуре их нет).

Есть только в контуре (снаружи заменено заглушками, в снимке остаётся как есть):

  • Nexus nexus.dev.raisa.go.rshbank.ru (setup/pip.conf, setup/.npmrc) и пакеты raisa_fastapi_protected_api (api/requirements.txt arm-core), raisa-fastapi-protected-web, raisa-ui (web/package.json, optionalDependencies). Снаружи make venv их пропускает (grep -v '^raisa_'), импорт raisa_fastapi_protected_api условный — только при DEBUG=false (в контуре временно DEBUG=true, см. 6.3). В контуре всё ставится штатно.
  • Запуск платформой: api/run_server.py, SERVER__HOST/SERVER__PORT, ресурсы raisa_config.yml, секреты и настройки OPENBAO__* из OpenBao (раздел 6), Python 3.11 в базовом образе.
  • PostgreSQL 16 (схема dkb), NATS без JetStream (nkeys/JWT), S3-совместимое хранилище, вход временно через dev-заглушку, позже JWT РАИСА (раздел 6.3).

3. Подготовка снимка: scripts/prepare-contour-snapshot.sh

Запуск из workspace, по одному репозиторию:

docs/scripts/prepare-contour-snapshot.sh arm-core        /tmp/contour
docs/scripts/prepare-contour-snapshot.sh tabular-parser  /tmp/contour
docs/scripts/prepare-contour-snapshot.sh scan-gateway    /tmp/contour
# третий аргумент — ref; по умолчанию origin/main (снимок всегда с сервера, не из рабочего дерева)

Что делает скрипт (репозиторий-источник не меняется):

  1. git archive <ref> → /tmp/contour/<repo>/ — дерево коммита без .git и без игнорируемых файлов.
  2. Удаляет .claude/, CLAUDE.md, openspec/config.yaml, docs/superpowers/, Dockerfile, .dockerignore.
  3. Правит Makefile: цель venv → python3.11 -m venv + pip install -r requirements-dev.txt с setup/pip.conf (переменная PYTHON, по умолчанию python3.11); удаляет цель docker-build и её упоминания в check-full, .PHONY, help.
  4. Заменяет ссылки на внешний Gitea на RSHB/<repo>#<n>.
  5. Пишет SNAPSHOT.md (источник, хеш, дата, что вырезано).
  6. Проверяет: нет .claude/CLAUDE.md/openspec/config.yaml/планов/Dockerfile, нет локальных путей (/Users/…, /home/…), нет uv и git.aepif.ru, на месте платформенные файлы (raisa_config.yml, setup/, run_server.py, requirements.txt, .env.test, contracts/). Любая проблема — код возврата 1 и список.

Проверено 06.10.2026: снимки arm-core@1d7f2c8, tabular-parser@3b32462, scan-gateway@dffc18a проходят make check без docs и fixtures (1084 / 1087 / 726 passed, пропуски только по причинам из раздела 5).

4. Как положить снимок в контур

Репозитории контура — проекты GitLab, по одному на сервис, имена как снаружи. Первый перенос и каждый следующий делаются одинаково (вместо PR в GitLab — merge request, в остальном git тот же):

git clone <url контура>/arm-core contour-arm-core
cd contour-arm-core && git switch -c sync/arm-core-1d7f2c8      # ветка по правилам контура
rsync -a --delete --exclude .git /tmp/contour/arm-core/ ./          # дерево = снимок, удалённые снаружи файлы исчезают
git status --short                                                  # прочитать diff: он должен совпадать с изменениями снаружи
git add -A
git commit -m "sync: arm-core@1d7f2c8 (2026-10-06)"                 # хеш и дата — из SNAPSHOT.md
git push -u origin sync/arm-core-1d7f2c8                           # дальше merge request и мерж по регламенту GitLab контура

--delete обязателен: иначе файлы, удалённые или переименованные снаружи, останутся в контуре. Если в контуре делали локальные правки (раздел 7), сначала вернуть их наружу, иначе rsync --delete их перезапишет.

Порядок сервисов при одной синхронизации: arm-core → tabular-parser → scan-gateway (ядро — потребитель контракта, его копия схемы должна быть не старше копий сервисов). Если снаружи поднималась версия контракта (api/contracts/*.json, CONTRACT_VERSION в api/src/contract/common.py), переносить все три сервиса одним заходом.

5. Проверка в контуре перед merge request

make venv            # python3.11 -m venv + pip через Nexus (вместо setup/setup.sh; тот ставит только прод-зависимости)
make check           # ruff, mypy, pytest без интеграционных тестов

Ожидаемые пропуски (pytest -rs покажет причины), это не ошибки:

Сервис Пропускается Причина
все тест идентичности api/contracts/ с docs/contracts/ docs нет; идентичность проверена снаружи перед снимком
все тесты с маркером fixtures (golden-снимки, property-тесты на реальных ОСВ/формах, покрытие словарей) fixtures нет; регрессия на реальных данных — снаружи
arm-core сверка ORM с DBML в тестах DBML в docs
scan-gateway тесты с реальными ответами ИС Сканирование (fixtures/ocr) то же

Не запускать в контуре: make up, make test-integration, make check-full, make dbml-check, make e2e — им нужны compose из docs либо Docker. Интеграционные тесты против PostgreSQL/NATS/MinIO прогоняются снаружи перед каждым снимком; пайплайну платформы отдаётся make check.

Фронт arm-core: make web-check (npm install через .npmrc Nexus, biome, tsc, npm run build). В контуре raisa-fastapi-protected-web и raisa-ui ставятся штатно, снаружи они в optionalDependencies и пропускаются. web/package-lock.json снаружи не коммитится (OQ-63) — в контуре lock-файл появится при первом npm install, решение о его коммите за контуром.

6. Что нужно от платформы для запуска

6.1. Как задаются настройки: только через OpenBao

Платформа РАИСА передаёт в под только секреты OpenBao; обычные переменные окружения (DEBUG, NATS_ENABLED, ...) задать нельзя (ответ разработчика переноса, 07.10.2026). Поэтому любая настройка верхнего уровня Settings задаётся как OPENBAO__APP__<ИМЯ> (arm-core PR #49, issue #44; arm-scan-gateway PR #12, issue #11; arm-tabular-parser PR #23, issue #22; файл api/src/core/settings_sources.py одинаков в трёх сервисах). Правила:

  • приоритет источников: обычная переменная окружения → OPENBAO__APP__* → api/.env → значение по умолчанию;
  • OPENBAO__APP__* читается только из окружения процесса, в .env не действует;
  • имя должно существовать в Settings; неизвестное имя — ошибка при старте (опечатка не остаётся незамеченной);
  • вложенные настройки — через __: OPENBAO__APP__SERVER__PORT;
  • OPENBAO__APP__OPENBAO__... запрещено: секреты задаются только как OPENBAO__<ГРУППА>__<ИМЯ>.

Имена и смысл настроек — api/.env.example каждого сервиса. Секреты (общие для всех сервисов: OPENBAO__MINIO__*, OPENBAO__NATS__*) задаются как раньше.

6.2. Значения контура по сервисам

Сервис Секреты OPENBAO__<ГРУППА>__* Настройки OPENBAO__APP__*
arm-core OPENBAO__DATABASE__DATABASE_URL (asyncpg, схема dkb), OPENBAO__MINIO__{MINIO_URL, MINIO_ADMIN_LOGIN, MINIO_ADMIN_PASSWORD, MINIO_BUCKET} (MINIO_SECURE=true по умолчанию), OPENBAO__NATS__{NATS_URL, NATS_SEED, NATS_JWT} NATS_ENABLED=true (по умолчанию false: без него NATS не подключается и обмен с сервисами распознавания не работает), DEBUG=true, AUTH_DEV_ISSUER=true, AUTH_DEV_SECRET=<случайная строка от 32 байт> (поле верхнего уровня Settings, поэтому тоже через OPENBAO__APP__), FORWARDED_ALLOW_IPS=<адреса прокси платформы> (иначе IP в аудите — адрес прокси); CORS_ORIGINS при DEBUG=true не нужен. SERVER__HOST не задавать (платформа задаёт сама)
scan-gateway OPENBAO__MINIO__*, OPENBAO__NATS__*, OPENBAO__SMB__{SMB_HOST, SMB_PATH, SMB_LOGIN, SMB_PASSWORD} SCAN_HOTFOLDER_MODE=smb (NATS_ENABLED по умолчанию true, задавать не нужно). SCAN_HOTFOLDER_PATH в контуре не нужен (только режим local; режим local и эмулятор make emulator — для разработки)
tabular-parser OPENBAO__MINIO__*, OPENBAO__NATS__* сверх секретов ничего (NATS_ENABLED по умолчанию true); при необходимости PARSE_TIMEOUT_SECONDS (и PARSE_MAX_BYTES)

В отличие от arm-core, в scan-gateway и tabular-parser NATS_ENABLED=true по умолчанию, а DEBUG влияет только на уровень логов; платформенной аутентификации у них нет (внутренние сервисы без HTTP-входа для пользователей).

Зависимости под контур: PyJWT[crypto]==2.8.0 и cryptography==43.0.3 (arm-core PR #50, issue #43; в том же PR исправлена проверка JWKS на PyJWT 2.8.0).

Миграции и seed при деплое: из api/ — alembic upgrade head, затем python -m scripts.seed (справочники: план счетов, правила элиминации, роли и права; --dev только для стенда разработки). Те же команды — цели make migrate и make seed (README.md arm-core, «Быстрый старт», шаг 4 «Миграции и справочники»).

Subjects NATS, схема ключей S3 и ресурсы — context/03-architecture.md.

6.3. Аутентификация в контуре (временно)

Контур временно без платформенной аутентификации: приложение доступно любому, кто его открыл. Вход идёт через dev-заглушку: POST /api/v1/dev/token {email} выдаёт JWT, на фронте её использует форма DevLogin. Что для этого включено:

  • OPENBAO__APP__DEBUG=true дополнительно включает CORS * и открытые /docs, /redoc, /openapi.json; middleware raisa_fastapi_protected_api не импортируется (в arm-core он и так не подключён);
  • OPENBAO__APP__AUTH_DEV_ISSUER=true подключает роутер dev-токенов (нужны оба флага: DEBUG и AUTH_DEV_ISSUER); токены подписываются AUTH_DEV_SECRET.

Последствия, которые нужно понимать:

  • пользователи с ролями должны быть в app_user до входа (seed); войти может любой, кто знает email пользователя, то есть защита контура держится на сетевой изоляции, а не на аутентификации;
  • AUTH_DEV_SECRET — секрет: случайная строка от 32 байт, хранится в OpenBao, в код и в .env.example не попадает.

Переход на SSO РАИСА (отключить DEBUG/AUTH_DEV_ISSUER, задать AUTH_JWKS_URL или AUTH_PUBLIC_KEY + AUTH_ISSUER, AUTH_AUDIENCE, AUTH_EMAIL_CLAIM) — после ответа на OQ-54 (context/07-open-questions.md) и задачи arm-core#43.

7. Правки внутри контура и обратный поток

Правило: источник истины — репозитории снаружи. Любая правка в контуре (чаще всего под пайплайн платформы: пути, переменные, raisa_config.yml) возвращается наружу до следующей синхронизации:

cd contour-arm-core
git diff <коммит sync> HEAD -- . ':!SNAPSHOT.md' > /tmp/contour-arm-core.patch   # база — коммит последнего sync

Патч применяется снаружи в ветку и проходит обычный PR; после его мержа следующий снимок уже содержит правку, и rsync --delete ничего не потеряет. Если правка касается только контура (например, секретов платформы), её место — вне репозитория: значения в OpenBao (OPENBAO__*), а не код.

8. Чек-лист одной синхронизации

  1. Снаружи: нужные PR слиты в main трёх сервисов; make check-full зелёный в каждом; контракт в docs/contracts и копии api/contracts идентичны.
  2. Правки из контура (раздел 7) возвращены наружу и слиты.
  3. prepare-contour-snapshot.sh по каждому сервису; скрипт завершился словами «снимок готов».
  4. В контуре: клон, ветка, rsync --delete, просмотр git status, коммит sync: <repo>@<hash> (<дата>), make venv, make check (пропуски — только из раздела 5), для arm-core make web-check, merge request по регламенту GitLab контура.
  5. Порядок мержа при смене версии контракта: arm-core → tabular-parser → scan-gateway.
  6. Снаружи в context/09-delivery-plan.md или в issue синхронизации отметить, какие хеши перенесены.

9. Известные неточности снимка

  • README.md сервисов описывают make venv через uv и ссылаются на ../docs, ../fixtures, docs/dev/qa-guide.md; скрипт их не переписывает. Ориентир для контура — этот документ и SNAPSHOT.md.
  • В openspec/changes/archive/*/ остаются относительные пути вида ../docs/openspec/... и ../fixtures/...: это ссылки на внешний workspace, в контуре они никуда не ведут.
  • Тесты, которые снаружи пропускаются без fixtures, в контуре не выполняются никогда. Поэтому перед каждым снимком снаружи обязателен make check-full с доступными docs и fixtures (dev/checks.md, раздел «Порядок перед PR»).