28 KiB
Перенос сервисов АРМ КА в контур банка: гайд для разработчика
Разработка ведётся снаружи (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/ (и web/openspec/… в arm-core) |
да | спецификация системы и журнал решений (design.md); без них код в контуре нечем объяснять |
.claude/, .codegraph/, CLAUDE.md, openspec/config.yaml — на любой глубине, в т.ч. web/ arm-core |
нет | инструкции и контекст 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/,.codegraph/,CLAUDE.md,openspec/config.yaml) вырезается на любой глубине, в том числе во вложенномarm-core/web/; удалять их вручную не нужно.- Внешний 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.txtarm-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 (снимок всегда с сервера, не из рабочего дерева)
Что делает скрипт (репозиторий-источник не меняется):
- Для каждого сервиса, где в
<ref>естьapi/arm_statements/: проверяет свежесть копии пакета (sync-statements.sh --checkпо дереву<ref>). Отстаёт — предупреждение и абзац вSNAPSHOT.md; копия правлена напрямую или чужая — снимок не делается (код 1); нетarm-statementsрядом или подходящего Python — предупреждение, снимок делается. Нетapi/arm_statements/— проверка молча пропускается. git archive <ref>→/tmp/contour/<repo>/— дерево коммита без.gitи без игнорируемых файлов.- Удаляет
.claude/,.codegraph/,CLAUDE.md,openspec/config.yaml,docs/superpowers/на любой глубине (в т.ч.web/arm-core; опустевшийdocs/тоже),Dockerfileи.dockerignore— в корне. - Правит
Makefile: цельvenv→python3.11 -m venv+pip install -r requirements-dev.txtсsetup/pip.conf(переменнаяPYTHON, по умолчаниюpython3.11); удаляет цельdocker-buildи её упоминания вcheck-full,.PHONY,help. - Заменяет ссылки на внешний Gitea на
RSHB/<repo>#<n>. - Только arm-core: урезает
web/package.jsonдо зависимостей сборки (раздел 3.2) и правит цельweb-check. - Пишет
SNAPSHOT.md(источник, хеш, дата, что вырезано и какие пакеты/скрипты фронта убраны). - Только arm-core: собирает урезанный
web/(npm install+npm run buildво временной копии); не собралось — снимок не выдаётся (код 1, «пакет, нужный сборке, не в разрешённом списке»). Для тестов без сети проверку отключаетSNAPSHOT_SKIP_WEB_BUILD=1. - Проверяет: нет
.claude/.codegraph/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 и список.
Тест скрипта — make test-snapshot (синтетический репозиторий с артефактами агента в корне и в web/); проверка
копии arm_statements (пропуск, отставание, правка)
— в make test-snapshot (нужен Python ≥ 3.11 с PyYAML, make venv).
3.1. Фронт arm-core: web/ ведётся в arm-core
С 21.10.2026 фронт разрабатывается прямо в arm-core/web (PR в RSHB/arm-core → main), отдельный репозиторий
RSHB/arm-web заморожен на test de4e477 (= arm-core 3157a15), скрипт синхронизации sync-web.sh удалён
(docs/context/00-workspace-layout.md). Снимок arm-core берёт web/ как есть из <ref>; флаг --web-as-is,
нужный раньше для переноса с отставанием от arm-web, игнорируется с предупреждением.
Проверено 06.10.2026: снимки arm-core@1d7f2c8, tabular-parser@3b32462, scan-gateway@dffc18a проходят make check без
docs и fixtures (1084 / 1087 / 726 passed, пропуски только по причинам из раздела 5).
3.2. Урезанный web/package.json в снимке arm-core
В контуре фронт только собирается (npm install через Nexus + rsbuild build). Зеркало npm банка не содержит свежих
версий инструментов разработки (уже ломались на vitest — в зеркале ≤3.1.4; затем eslint, typescript-eslint, prettier из
arm-web #29), поэтому снимок оставляет только нужное сборке (решение владельца 16.10.2026, AURORA-1635, issue docs #112).
Разработка и проверки фронта (vitest, eslint, prettier, biome, tsc) идут снаружи, где npm доступен.
Разрешённый список — массивы WEB_DEV_ALLOW, WEB_OPTIONAL_KEEP_PREFIX, WEB_SCRIPTS_KEEP в начале
prepare-contour-snapshot.sh:
Раздел package.json |
Остаётся | Убирается |
|---|---|---|
dependencies |
всё | — |
devDependencies |
@rsbuild/core, @rsbuild/plugin-react, tailwindcss, daisyui (rsbuild.config.ts, postcss.config.cjs, tailwind.config.js) |
всё остальное: eslint и плагины, prettier, biome, typescript, @types/*, vitest, globals |
optionalDependencies |
raisa-* |
инструменты разработки (@biomejs/cli-linux-x64-musl) |
scripts |
build, start, preview |
test, lint, format и любые новые |
make web-check в снимке — только npm install и npm run build (без biome и tsc); check-full ссылается на него как
раньше. Конфиги инструментов (biome.json, eslint.config.js, tsconfig.json) в web/ остаются, сборка их не читает.
Что убрано в конкретном снимке — в SNAPSHOT.md.
Защита от обратной ошибки: скрипт сам собирает урезанный web/ во временной копии (снимок остаётся без
node_modules/dist/package-lock.json). Если сборка не прошла, значит ей понадобился пакет вне списка: добавьте его в
WEB_DEV_ALLOW и повторите снимок.
Правило обратного потока: web/package.json из контура наружу не переносить — он урезан (нет eslint, prettier, vitest,
typescript и скриптов test/lint/format); источник истины — web/package.json в arm-core main снаружи (раздел 3.1).
Правки зависимостей в контуре возвращаются в arm-core вручную, только по существу, а не файлом целиком.
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 и npm run build; biome и tsc убраны вместе с пакетами, раздел 3.2). В контуре
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_TABULAR_REQUEST_SUBJECT, NATS_IMAGE_REQUEST_SUBJECT, NATS_RESULT_SUBJECT} |
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 не задавать (платформа задаёт сама). Временно, на период тестов: CONSOLIDATION_PERIOD_FROM_VERSION=true — ОСВ, загруженная в консолидацию, берёт её период (arm-core#103, OQ-161; qa-guide.md §7.4); после тестов убрать |
| scan-gateway | OPENBAO__MINIO__*, OPENBAO__NATS__* (включая три subject'а), 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__* (включая три subject'а) |
сверх секретов ничего (NATS_ENABLED по умолчанию true); при необходимости PARSE_TIMEOUT_SECONDS (и PARSE_MAX_BYTES) |
Для тестового контура — свой набор JWT, seed и трёх subject'ов (NATS_TABULAR_REQUEST_SUBJECT по умолчанию
files.tabular_uploaded, NATS_IMAGE_REQUEST_SUBJECT — files.img_uploaded, NATS_RESULT_SUBJECT —
files.tabular_parsed): одинаковый у всех трёх сервисов; JWT должен давать publish/subscribe на эти subject'ы.
Разъехавшиеся настройки — тишина на шине (dlq по NO_ACK); сверяйте /healthcheck.subjects.
В отличие от 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; middlewareraisa_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. Чек-лист одной синхронизации
- Снаружи: нужные PR слиты в
mainтрёх сервисов;make check-fullзелёный в каждом; контракт вdocs/contractsи копииapi/contractsидентичны. - Правки из контура (раздел 7) возвращены наружу и слиты.
prepare-contour-snapshot.shпо каждому сервису; скрипт завершился словами «снимок готов».- В контуре: клон, ветка,
rsync --delete, просмотрgit status, коммитsync: <repo>@<hash> (<дата>),make venv,make check(пропуски — только из раздела 5), для arm-coremake web-check, merge request по регламенту GitLab контура. - Порядок мержа при смене версии контракта: arm-core → tabular-parser → scan-gateway.
- Снаружи в
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»).