# Перенос сервисов АРМ КА в контур банка: гайд для разработчика Разработка ведётся **снаружи** (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`. В контуре всё ставится штатно. - Запуск платформой: `api/run_server.py`, `SERVER__HOST`/`SERVER__PORT`, ресурсы `raisa_config.yml`, секреты `OPENBAO__*` из OpenBao, Python 3.11 в базовом образе. - PostgreSQL 16 (схема `dkb`), NATS без JetStream (nkeys/JWT), S3-совместимое хранилище, JWT РАИСА для входа. ## 3. Подготовка снимка: `scripts/prepare-contour-snapshot.sh` Запуск из workspace, по одному репозиторию: ```bash 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 ` → `/tmp/contour//` — дерево коммита без `.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/#`. 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 тот же): ```bash git clone /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 ```bash 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. Что нужно от платформы для запуска Переменные окружения — `api/.env.example` каждого сервиса (имена и смысл), секреты под префиксом `OPENBAO__*`. Минимум для 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}`. - `DEBUG=false` (включает middleware РАИСА, выключает `/docs` и dev-токены), `CORS_ORIGINS` (явный список; пусто — кросс-доменные запросы запрещены), `FORWARDED_ALLOW_IPS` (адреса прокси платформы, иначе IP в аудите — адрес прокси), `AUTH_JWKS_URL` или `AUTH_PUBLIC_KEY` + `AUTH_ISSUER`, `AUTH_AUDIENCE`, `AUTH_EMAIL_CLAIM` — параметры JWT РАИСА (открытый вопрос OQ в `context/07-open-questions.md`). `AUTH_DEV_ISSUER`/`AUTH_DEV_SECRET` в проде не задавать. - Миграции и seed при деплое: из `api/` — `alembic upgrade head`, затем `python -m scripts.seed` (справочники: план счетов, правила элиминации, роли и права; `--dev` только для стенда разработки). Те же команды — цели `make migrate` и `make seed` (`README.md` arm-core, «Быстрый старт», шаг 4 «Миграции и справочники»). `tabular-parser` и `scan-gateway`: только `OPENBAO__MINIO__*`, `OPENBAO__NATS__*`, `DEBUG`, `NATS_ENABLED=true`; у парсера `PARSE_MAX_BYTES`, `PARSE_TIMEOUT_SECONDS`; у шлюза `SCAN_HOTFOLDER_MODE=smb` и `OPENBAO__SMB__{SMB_HOST, SMB_PATH, SMB_LOGIN, SMB_PASSWORD}` (режим `local` и эмулятор `make emulator` — только для разработки). Subjects NATS, схема ключей S3 и ресурсы — `context/03-architecture.md`. ## 7. Правки внутри контура и обратный поток Правило: источник истины — репозитории снаружи. Любая правка в контуре (чаще всего под пайплайн платформы: пути, переменные, `raisa_config.yml`) возвращается наружу до следующей синхронизации: ```bash cd contour-arm-core git diff <коммит sync> HEAD -- . ':!SNAPSHOT.md' > /tmp/contour-arm-core.patch # база — коммит последнего sync ``` Патч применяется снаружи в ветку и проходит обычный PR; после его мержа следующий снимок уже содержит правку, и `rsync --delete` ничего не потеряет. Если правка касается только контура (например, секретов платформы), её место — вне репозитория: переменные окружения 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: @ (<дата>)`, `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»).