docs: Срез 0 под факты платформы — NATS без JetStream, Python 3.11, PostgreSQL 16, без CI и Nexus
По ответам пользователя 03.10.2026: обмен через recognition_job перестроен на core NATS (queue group'ы, ack сервиса = конверт status=processing, повторная публикация ядром 30 с × 3 → dlq, дедуп по result_msg_id); capability ci-template заменена на quality-checks (make check / make check-full, конфигураций CI нет); compose: PostgreSQL 16, NATS без --jetstream, python:3.11-slim; пакеты raisa_* фильтруются при dev-установке, npm raisa-* → optionalDependencies. Обновлены 03-architecture, 09-delivery-plan, 00-workspace-layout, proposal/design/specs/tasks change'а; открытые вопросы к платформе закрыты, остались Node и JWT РАИСА. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
5cbb23f011
commit
a0be5ab5a1
@ -28,8 +28,9 @@
|
||||
- **Ветки и коммиты — всегда `git -C <repo>`** или с явным `cd`. Хук `guard-main` должен определять репозиторий по
|
||||
пути изменяемого файла (`git -C "$(dirname "$FILE")" rev-parse --abbrev-ref HEAD`), а не по cwd workspace.
|
||||
- **codegraph** — по одному индексу на репозиторий; workspace-уровня карта кода не нужна.
|
||||
- **Относительные ссылки на контекст** — из любого репозитория: `../docs/context/...`, `../fixtures/...`. В CI сервисов
|
||||
фикстуры подтягиваются checkout'ом `arm-ka-fixtures` в `../fixtures` (или переменная `ARM_KA_FIXTURES`).
|
||||
- **Относительные ссылки на контекст** — из любого репозитория: `../docs/context/...`, `../fixtures/...`. Проверки
|
||||
(`make check`, `make check-full`) берут фикстуры из `../fixtures` или `ARM_KA_FIXTURES`, docs — из `../docs` или
|
||||
`ARM_KA_DOCS`; при отсутствии — пропуск с предупреждением. CI в Gitea нет; пайплайн платформы банка — вне репозиториев.
|
||||
- **Вложенные `CLAUDE.md`** в каждом репозитории короткие: что это за сервис, его собственные соглашения, ссылка на
|
||||
`../CLAUDE.md` и `../docs/`. Шаблоны — `docs/repo-templates/`.
|
||||
- Один `claude`-сеанс может править несколько репозиториев, но PR — по одному на репозиторий; в описании PR указывать
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
│ │
|
||||
PostgreSQL │ │ S3 (uploads/ results/ exports/ archive/)
|
||||
│ │
|
||||
NATS JetStream ◀─────────────────┘ outbox │
|
||||
NATS (core) ◀─────────────────┘ outbox │
|
||||
recognition.tabular.request │
|
||||
recognition.image.request │
|
||||
recognition.result ───────────────────▶ arm-core (inbox, дедуп по msg_id)
|
||||
@ -47,14 +47,21 @@
|
||||
| результат | `recognition.result` | tabular-parser / scan-gateway | `schema_version`, `job_id`, `file_id`, `status` (done / needs_check / error), `error {code, message}`, `service {name, version}`, `result_s3 {bucket, key}`, `result_schema_version`, `raw_s3_key` (только scan-gateway), `metrics` | `result:<job_id>:<attempt>` |
|
||||
|
||||
`group_schema` в конверт не входит — ядро берёт его из `documents[].group_schema` файла результата при маппинге.
|
||||
Один stream `RECOGNITION` (subjects `recognition.>`); durable consumer'ы: `tabular-parser` и `scan-gateway` — запросы,
|
||||
`arm-core-results` — результаты.
|
||||
Конверт со `status = processing` — подтверждение взятия в работу (ack), `Nats-Msg-Id = ack:<job_id>:<attempt>`.
|
||||
|
||||
**На платформе NATS без JetStream** (ответ 03.10.2026): нет durable consumer'ов, серверного дедупа и redelivery.
|
||||
Поэтому гарантии доставки — на уровне приложения: сервисы подписаны queue group'ами (`tabular-parser`, `scan-gateway`),
|
||||
ядро — queue group `arm-core` на `recognition.result`; сервис сразу по получении запроса публикует ack `processing`;
|
||||
ядро, не получив ack за `ACK_TIMEOUT_SECONDS` (30), публикует запрос повторно с тем же `Nats-Msg-Id`, не более
|
||||
`OUTBOX_MAX_REPUBLISH` (3) раз, затем `dlq`. Сервис держит process-local множество `job_id` в работе и на повторный
|
||||
запрос отвечает ack'ом без второй обработки. Появится JetStream — меняются только publisher/subscriber.
|
||||
|
||||
Правила:
|
||||
- `recognition_job` создаётся в статусе `queued` **в одной транзакции** со `stored_file`; фоновый публикатор переводит в
|
||||
`published` (`request_msg_id` = `Nats-Msg-Id`). Повтор публикации — по `queued` старше N секунд.
|
||||
- Результат принимается через durable consumer; `result_msg_id UNIQUE` = дедуп. После `max_deliver` → `dlq`;
|
||||
replay = сброс в `queued` с `attempt + 1`.
|
||||
`published` (`request_msg_id` = `Nats-Msg-Id`), ack сервиса — в `processing` (`started_at`). Повтор публикации — по
|
||||
`queued` старше N секунд и по `published` без ack.
|
||||
- Результат принимается подпиской queue group; `result_msg_id UNIQUE` = дедуп. Нет ack после повторов → `dlq`;
|
||||
потерянный результат → `reply_deadline` → `timeout`; replay = сброс в `queued` с `attempt + 1`.
|
||||
- Результат одного файла содержит **N документов**; ядро на каждый создаёт `document` и маппит данные.
|
||||
- Сервисы не знают про БД и про бизнес-смысл: они возвращают структуру файла; нормализация счетов, резолв
|
||||
контрагентов, проверки — в ядре.
|
||||
@ -184,8 +191,8 @@ FastAPI» платформы РАИСА/aurora): дерево импортиро
|
||||
- **S3** — `miniopy-async` (`Minio`, `cert_check=False`), **один бакет** `MINIO_BUCKET`; роли `s3_object.bucket_role`
|
||||
становятся первым сегментом ключа (`uploads/…`, `results/…`, `raw/…`, `exports/…`, `archive/…`).
|
||||
- **NATS** — `nats-py`; аутентификация nkeys-seed + JWT (`NATS_SEED`, `NATS_JWT`) через `user_jwt_cb`/`signature_cb`;
|
||||
соединение и подписки создаются в lifespan и живут в `app.state.nats` / `app.state.nats_subs`. JetStream
|
||||
(durable consumer, `Nats-Msg-Id`) — поверх того же соединения.
|
||||
соединение и подписки создаются в lifespan и живут в `app.state.nats` / `app.state.nats_subs`. Core NATS без
|
||||
JetStream: подписки с queue group, заголовки `Nats-Msg-Id` ставим сами, ack и повторы — на уровне приложения.
|
||||
- **SMB** (горячие папки ИС Сканирование) — `smbprotocol`/`smbclient`, сессия регистрируется в lifespan; нужен только `scan-gateway`.
|
||||
- **Внешняя аутентификация** — пакет `raisa_fastapi_protected_api` из внутреннего Nexus (`AuthorizationMiddleware`,
|
||||
`ProtectedSettings`, …), импортируется только при `DEBUG=false`; фронтовая пара — `raisa-fastapi-protected-web`.
|
||||
@ -218,13 +225,21 @@ FastAPI» платформы РАИСА/aurora): дерево импортиро
|
||||
### Сборка и деплой
|
||||
|
||||
- Платформа РАИСА запускает `api/run_server.py` с `SERVER__HOST`/`SERVER__PORT` из окружения; ресурсы — `raisa_config.yml`.
|
||||
Python на платформе — **3.11** (ответ 03.10.2026); версия Node неизвестна (локально `node:22`).
|
||||
- `Dockerfile`, `docker-compose.yml`, `.dockerignore` шаблон держит в `.gitignore` («Custom ignore»): образ собирает
|
||||
платформа. Для локальной разработки и CI: compose — на уровне workspace (`docs/dev/docker-compose.yml`), `Dockerfile` —
|
||||
в корне каждого сервиса, строка `Dockerfile` убирается из `.gitignore` (единственная правка шаблона; решение Среза 0).
|
||||
платформа своим CI/CD, в который мы не лезем. Для локальной разработки: compose — на уровне workspace
|
||||
(`docs/dev/docker-compose.yml`), `Dockerfile` (`python:3.11-slim`) — в корне каждого сервиса, строка `Dockerfile`
|
||||
убирается из `.gitignore` (единственная правка шаблона; решение Среза 0).
|
||||
- Фронт: `npm run build` (rsbuild) → `web/dist/` → копируется в `api/web/`, откуда FastAPI раздаёт SPA; роутеры
|
||||
подключаются **до** mount'а `/`.
|
||||
- Зеркала пакетов: pip и npm настроены на `nexus.dev.raisa.go.rshbank.ru` (`setup/pip.conf`, `setup/.npmrc`).
|
||||
- CI-конфига в шаблоне нет; шаблон пайплайна — `docs/dev/ci/` (change `slice-0-skeleton` в docs).
|
||||
- Зеркала пакетов: pip и npm настроены на `nexus.dev.raisa.go.rshbank.ru` (`setup/pip.conf`, `setup/.npmrc`). Вне банка
|
||||
Nexus недоступен до прода: dev-установка ставит `requirements.txt` без строк `raisa_*` (`make venv`), в
|
||||
`web/package.json` пакеты `raisa-*` переносятся в `optionalDependencies`; импорты `raisa_fastapi_protected_api` условные
|
||||
(`DEBUG=false`), как в шаблоне.
|
||||
- CI в Gitea нет и не планируется; конфигураций CI в репозиториях нет. Проверки качества — одинаковые цели `Makefile`
|
||||
во всех репозиториях: `make check` (ruff, mypy, pytest без Docker, идентичность контракта) и `make check-full`
|
||||
(+ интеграционные тесты против compose, для arm-core — цикл миграций и `check_dbml_vs_orm.py`); `make check-full`
|
||||
обязателен перед PR. Описание — `docs/dev/checks.md`.
|
||||
|
||||
### Frontend (только arm-core)
|
||||
|
||||
@ -253,7 +268,8 @@ FastAPI» платформы РАИСА/aurora): дерево импортиро
|
||||
|
||||
## Что ожидаем от платформы (для запроса ресурсов)
|
||||
|
||||
PostgreSQL 15+ (одна БД, схема `dkb` — как в шаблоне), NATS JetStream (один stream `RECOGNITION`, 3 subject;
|
||||
аутентификация nkeys/JWT, как в шаблоне), S3-совместимое хранилище (один бакет; префиксы-роли + lifecycle по префиксам),
|
||||
Nexus (pip/npm, пакеты `raisa_*`), Gitea + CI (lint, pytest, миграции с нуля, сборка образов), реестр образов.
|
||||
Секреты — через переменные окружения (`OPENBAO__*`).
|
||||
По ответам платформы (03.10.2026): PostgreSQL **16** (одна БД, схема `dkb` — создавать схемы можно), NATS **без
|
||||
JetStream** (3 subject `recognition.*`; аутентификация nkeys/JWT, как в шаблоне), S3-совместимое хранилище (один бакет;
|
||||
префиксы-роли + lifecycle по префиксам), Python 3.11 в базовом образе, Nexus (pip/npm, пакеты `raisa_*`) — только внутри
|
||||
банка, CI/CD — пайплайн платформы (мы отдаём команды `make check` / `make check-full`). Секреты — через переменные
|
||||
окружения (`OPENBAO__*`). Открыто: версия Node, параметры JWT РАИСА.
|
||||
|
||||
@ -13,14 +13,15 @@
|
||||
- Три репозитория из нулевого коммита шаблона команды; соглашения шаблона — в `03-architecture.md` и в `CLAUDE.md`
|
||||
каждого репозитория. **Первая задача агента — прочитать шаблон и дополнить документы.** (Сделано: раздел «Соглашения
|
||||
шаблона команды» в `03-architecture.md`; в `tabular-parser`/`scan-gateway` фронт и БД вырезаются задачами среза.)
|
||||
- OpenSpec change'и среза: `docs/openspec/changes/slice-0-skeleton` (сквозной: контракт, compose, CI-шаблон, документы) и
|
||||
- OpenSpec change'и среза: `docs/openspec/changes/slice-0-skeleton` (сквозной: контракт, compose, проверки качества, документы) и
|
||||
по одному `openspec/changes/slice-0-skeleton` в `arm-core`, `tabular-parser`, `scan-gateway`; issue #1 в каждом репозитории.
|
||||
- `docker-compose` workspace-уровня (`docs/dev/docker-compose.yml`): PostgreSQL 15, NATS 2.10 JetStream, MinIO (S3, один
|
||||
бакет `arm-ka`), эмулятор ИС Сканирование (часть `scan-gateway`, отдаёт `fixtures/ocr/aep_N.json`), три сервиса (профиль
|
||||
`services`). `Makefile` с `up/test/lint/migrate` в каждом репо (шаблон Makefile не содержит — добавляется).
|
||||
- `docker-compose` workspace-уровня (`docs/dev/docker-compose.yml`): PostgreSQL 16, NATS 2.10 **без JetStream** (как на
|
||||
платформе; ack и повторная публикация — на уровне приложения), MinIO (S3, один бакет `arm-ka`), эмулятор ИС
|
||||
Сканирование (часть `scan-gateway`, отдаёт `fixtures/ocr/aep_N.json`), три сервиса (профиль `services`, Python 3.11).
|
||||
`Makefile` с общими целями `up/test/test-integration/lint/check/check-full` (+ `migrate/seed` в ядре) в каждом репо.
|
||||
- Контракт v1.0 — три схемы в `docs/contracts/`: `recognition-request`, `recognition-result-message` (конверты NATS) и
|
||||
`recognition-result` (файл результата) + `examples/`. Общий пакет `arm-ka-contracts` **не публикуется**: каждый сервис
|
||||
хранит копию схем в `api/contracts/` и pydantic-модели в `src/contract/`, идентичность с `docs/contracts` проверяется в CI
|
||||
хранит копию схем в `api/contracts/` и pydantic-модели в `src/contract/`, идентичность с `docs/contracts` проверяется в `make check`
|
||||
(решение D-1 change `slice-0-skeleton` в docs).
|
||||
- Alembic: миграция 0001 — **все** таблицы и enum'ы DBML v5.13 (черновик из ORM, сверка с DBML скриптом
|
||||
`api/scripts/check_dbml_vs_orm.py` — расхождение = ошибка CI).
|
||||
@ -29,9 +30,12 @@
|
||||
(План счетов 94н + типовые субсчета 1С, OQ-47), `elimination_rule` v1 из §4 (только ББ), `required_document_rule`
|
||||
для BASIC/EXTENDED (OQ-46).
|
||||
- Auth: валидация JWT + dev-эмитент; `app_user` seed (по одному на роль); зависимость `require(permission)`.
|
||||
- `audit_event` middleware/helper; `s3_object` клиент; outbox-паблишер и consumer `recognition.result` (пустые обработчики).
|
||||
- CI: lint, mypy, pytest, миграции с нуля и обратно, сборка образов.
|
||||
- DoD: `GET /api/v1/me` возвращает пользователя и его эффективные права; все миграции применяются; CI зелёный.
|
||||
- `audit_event` helper; `s3_object` клиент; outbox-паблишер (ack `processing`, повторная публикация без ack, `dlq`) и
|
||||
подписка `recognition.result` (дедуп по `result_msg_id`, пустой маппер документов).
|
||||
- Проверки качества вместо CI (в Gitea CI нет, пайплайн платформы вне репозиториев): `make check` (ruff, mypy, pytest,
|
||||
контракт) и `make check-full` (+ интеграционные тесты против compose, цикл миграций, `check_dbml_vs_orm`) — `docs/dev/checks.md`.
|
||||
- DoD: `GET /api/v1/me` возвращает пользователя и его эффективные права; все миграции применяются и откатываются;
|
||||
`make check-full` зелёный в трёх репозиториях.
|
||||
|
||||
## Срез 1 — Справочники (ГК / ЮЛ / ГСК / ГСЗ / филиалы) + пользователи и роли
|
||||
|
||||
|
||||
@ -7,16 +7,18 @@
|
||||
- Шаблон команды (`03-architecture.md`, «Соглашения шаблона команды»): один бакет S3, NATS с nkeys/JWT, настройки
|
||||
`OPENBAO__*`, `DEBUG=true` отключает внешние интеграции, `Dockerfile`/`docker-compose.yml` в `.gitignore` шаблона.
|
||||
- DECISION-4 DBML: сервисы распознавания без БД; обмен только через `recognition_job` (outbox/inbox), дедуп по `msg_id`, DLQ.
|
||||
- Внутренние пакеты банка (`raisa_*`) и зеркала Nexus недоступны вне сети банка; локальная разработка идёт на публичных
|
||||
образах и PyPI/npm.
|
||||
- Фикстуры — отдельный приватный репозиторий `arm-ka-fixtures`; в CI он нужен только read-only.
|
||||
- **Факты платформы (ответы пользователя 03.10.2026):** Python **3.11**; версия Node неизвестна; доступа к Nexus банка
|
||||
(`raisa_*`) не будет до деплоя в прод; CI в Gitea нет и не планируется — у платформы свой CI/CD, в него не лезем;
|
||||
**NATS без JetStream**; PostgreSQL **16**; создавать схемы можно; параметры JWT РАИСА пока неизвестны.
|
||||
- Фикстуры — отдельный приватный репозиторий `arm-ka-fixtures`; локально доступен как `../fixtures`.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** один файл контракта на каждое сообщение; один `docker compose up` для всего workspace; один шаблон CI, из
|
||||
которого три репозитория берут свои пайплайны; документы контекста соответствуют реальному шаблону.
|
||||
**Goals:** один файл контракта на каждое сообщение; один `docker compose up` для всего workspace; одна команда проверки
|
||||
качества, одинаковая для трёх репозиториев; документы контекста соответствуют реальному шаблону и реальной платформе.
|
||||
|
||||
**Non-Goals:** см. proposal. Дополнительно: не проектируем prod-деплой (это платформа банка) и не решаем ретеншн `results/` (ARCH-010).
|
||||
**Non-Goals:** см. proposal. Дополнительно: не проектируем prod-деплой (это платформа банка), не решаем ретеншн `results/`
|
||||
(ARCH-010), не пишем конфигурации CI ни для Gitea, ни для платформы.
|
||||
|
||||
## Decisions
|
||||
|
||||
@ -25,26 +27,39 @@
|
||||
`docs/contracts/*.schema.json` — источник истины; каждый сервис хранит копию в `api/contracts/` и pydantic-модели в
|
||||
`api/src/contract/`; тест сервиса сравнивает копию с `../docs/contracts` байт в байт (пропускается с предупреждением,
|
||||
если `docs` недоступен и не задан `ARM_KA_DOCS`), а pydantic-модели валидируют `docs/contracts/examples/*.json` и
|
||||
генерируют JSON, проходящий схему. *Альтернатива* — публикуемый пакет: нужен внутренний PyPI (вопрос к платформе),
|
||||
добавляет цикл релиза ради трёх потребителей. *Цена ошибки:* при переходе на пакет копии удаляются, тест идентичности
|
||||
заменяется пином версии — полдня.
|
||||
генерируют JSON, проходящий схему. *Альтернатива* — публикуемый пакет: нет внутреннего PyPI, добавляет цикл релиза ради
|
||||
трёх потребителей. *Цена ошибки:* при переходе на пакет копии удаляются, тест идентичности заменяется пином — полдня.
|
||||
|
||||
### D-2. Конверт результата не дублирует `group_schema`
|
||||
`03-architecture.md` перечислял `group_schema` в сообщении `recognition.result`. В конверте остаются только поля для
|
||||
маршрутизации и статуса; `group_schema` ядро берёт из `documents[].group_schema` файла результата при маппинге
|
||||
(Срез 2). *Почему:* одна истина на результат, сообщение остаётся маленьким (лимит NATS 1 МБ не трогаем).
|
||||
|
||||
### D-3. Идемпотентность — `Nats-Msg-Id` по естественному ключу попытки
|
||||
Запрос: `<job_id>:<attempt>`; результат: `result:<job_id>:<attempt>`. JetStream отбрасывает дубликаты в окне
|
||||
`duplicate_window` (2 мин по умолчанию); ядро дополнительно хранит `recognition_job.result_msg_id UNIQUE`
|
||||
(DBML) — повторная доставка после окна тоже безопасна.
|
||||
### D-3. Доставка без JetStream: app-level ack, повторная публикация ядром, дедуп в БД
|
||||
JetStream на платформе нет, значит нет durable consumer'ов, серверного дедупа по `Nats-Msg-Id`, redelivery и `max_deliver`.
|
||||
Схема на core NATS:
|
||||
- Запросы публикуются ядром из outbox (`recognition_job.status = queued → published`); сервисы подписаны **queue group**
|
||||
(`tabular-parser`, `scan-gateway`) — несколько инстанций делят нагрузку, одно сообщение получает одна.
|
||||
- Сервис **сразу по получении** публикует в `recognition.result` конверт со `status = processing` (ack: «взял в работу») —
|
||||
это значение уже есть в `recognition_status` DBML («сервис подтвердил взятие в работу»). Ядро ставит `processing`,
|
||||
`started_at`, `service_name/version`.
|
||||
- Если ack не пришёл за `ACK_TIMEOUT_SECONDS` (30) — ядро **публикует запрос повторно** с тем же `Nats-Msg-Id`
|
||||
(`<job_id>:<attempt>`, тот же `attempt`: попытка обработки не началась), не более `OUTBOX_MAX_REPUBLISH` (3) раз, затем
|
||||
`status = dlq` (DLQ-поведение DBML без брокера). `attempt`/`max_attempts` остаются для replay после `error`/`timeout`
|
||||
(ручной, Срез 3+).
|
||||
- Результат, потерянный при простое ядра, обнаруживается по `reply_deadline` → `timeout`; `result_msg_id UNIQUE` в
|
||||
`recognition_job` остаётся единственным дедупом результатов (`Nats-Msg-Id` сервисы проставляют сами).
|
||||
- Сервис держит process-local множество `job_id` в работе: повторный запрос с тем же `job_id` (пришёл повторно, пока
|
||||
первый обрабатывается или уже обработан в этом процессе) подтверждается ack'ом и не обрабатывается второй раз.
|
||||
*Альтернатива* — ждать JetStream или ставить свою очередь (Redis/БД-поллинг у сервисов): первое не в нашей власти, второе
|
||||
нарушает DECISION-4 (сервисы без БД). *Если JetStream появится:* меняются только publisher/subscriber (`js.publish`,
|
||||
durable consumer), outbox/inbox и контракт остаются.
|
||||
|
||||
### D-4. Один stream `RECOGNITION` с subjects `recognition.>`, два durable consumer'а запросов, один — результатов
|
||||
Запросы: consumer `tabular-parser` (filter `recognition.tabular.request`), `scan-gateway` (`recognition.image.request`),
|
||||
оба — pull, `ack_wait` ≥ `reply_deadline` сервиса, `max_deliver` = 3 (парсер) / 1 (OCR — ТЗ 3, OQ-15).
|
||||
Результаты: consumer `arm-core-results` (`recognition.result`), `max_deliver` = 5, после — DLQ-поведение по DBML
|
||||
(`status = dlq`). Кто создаёт stream: в dev — `arm-core` при старте (`NATS_MANAGE_STREAM=true`), в проде — по ответу
|
||||
платформы (Open Questions).
|
||||
### D-4. Subjects и группы
|
||||
Stream'ов и consumer'ов нет. Subjects: `recognition.tabular.request`, `recognition.image.request`, `recognition.result`.
|
||||
Queue group'ы: `tabular-parser`, `scan-gateway` — на запросах; `arm-core` — на результатах (несколько инстанций ядра
|
||||
получают результат по одному разу). Заголовки `Nats-Msg-Id` ставятся всегда (core NATS 2.2+ поддерживает заголовки) —
|
||||
для трассировки и будущего JetStream.
|
||||
|
||||
### D-5. Один бакет `arm-ka`, роли — префиксы
|
||||
Следствие шаблона (`MINIO_BUCKET`). Ключи: `uploads/{file_id}/{job_id}/{original_name}`,
|
||||
@ -59,44 +74,59 @@
|
||||
### D-7. Compose на уровне workspace, Dockerfile — в каждом сервисе
|
||||
`docs/dev/docker-compose.yml` собирает сервисы из `../../<repo>` (работает и из `.wt/<срез>/docs/dev`). Для этого из
|
||||
`.gitignore` сервисов убирается строка `Dockerfile` (единственная правка шаблона); `docker-compose.yml` в репозиториях
|
||||
не появляется. Образ собирает платформа по своим правилам — наш `Dockerfile` для dev и CI.
|
||||
не появляется. Образ для прода собирает платформа по своим правилам — наш `Dockerfile` только для dev и `make check-full`.
|
||||
|
||||
### D-8. CI — Gitea Actions, синтаксис GitHub Actions
|
||||
Шаблон `docs/dev/ci/python-service.yml` копируется в `<repo>/.gitea/workflows/ci.yml` и параметризуется именем
|
||||
сервиса. Фикстуры — `actions/checkout` репозитория `RSHB/arm-ka-fixtures` в `../fixtures` c токеном из секрета
|
||||
`FIXTURES_TOKEN`. Если раннер не поддерживает `services:` — fallback: `docker compose -f docs/dev/docker-compose.yml up`
|
||||
внутри job (вопрос к человеку).
|
||||
### D-8. Проверки качества — локальная команда, без конфигураций CI
|
||||
CI в Gitea нет, пайплайн платформы — вне репозиториев. Каждый репозиторий получает одинаковые цели `Makefile`:
|
||||
`make check` (быстро, без Docker: `ruff check`, `ruff format --check`, `mypy`, `pytest -m "not integration"`, тест
|
||||
идентичности контракта) и `make check-full` (`make check` + интеграционные тесты против compose; в `arm-core` — ещё
|
||||
«`alembic upgrade head` → `downgrade base` → `upgrade head`» и `check_dbml_vs_orm.py`). `make check-full` обязателен
|
||||
перед PR, результат вписывается в описание PR. Правила — `docs/dev/checks.md`. Фикстуры берутся из `../fixtures` или
|
||||
`ARM_KA_FIXTURES`; при отсутствии тесты с маркером `fixtures` пропускаются с предупреждением. Что из этого попадёт в
|
||||
пайплайн платформы — решает платформа; мы даём ей готовые команды.
|
||||
|
||||
### D-9. Версии инструментов
|
||||
Python **3.11** (`python:3.11-slim` в Dockerfile; пины `requirements.txt` совместимы). PostgreSQL **16** в compose
|
||||
(код не использует ничего новее 15; `NULLS NOT DISTINCT` доступен). NATS **2.10 без `--jetstream`** — паритет с прод.
|
||||
Node — неизвестен; локально `node:22` LTS, только для сборки `web/` в compose; меняется одной строкой.
|
||||
|
||||
### D-10. Пакеты `raisa_*` без Nexus
|
||||
`raisa_fastapi_protected_api` остаётся в `requirements.txt` для сборки платформой; dev-установка (`make venv`,
|
||||
`Dockerfile`) ставит `requirements.txt` без строк `raisa_*` (фильтр `grep -v`), код импортирует пакет условно, как в
|
||||
шаблоне (`DEBUG=false`). В `web/package.json` `raisa-ui` и `raisa-fastapi-protected-web` переносятся в
|
||||
`optionalDependencies`: `npm install` без Nexus проходит, с Nexus — ставит их как раньше. *Цена ошибки:* если платформа
|
||||
требует их в `dependencies` — вернуть две строки.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Публичные образы PostgreSQL/NATS/MinIO отличаются версиями от банковских] → версии фиксируются в compose (15 / 2.10 / RELEASE.2025-xx)
|
||||
и выравниваются по ответу платформы; код не использует фич новее PostgreSQL 15.
|
||||
- [Без JetStream сообщение теряется при простое получателя] → запросы: ack + повторная публикация ядром (D-3);
|
||||
результаты: `reply_deadline` → `timeout` и replay; для OCR повторная публикация до ack не повторяет распознавание
|
||||
(сервис ещё не брал файл).
|
||||
- [Повторный запрос пришёл в сервис, который уже обрабатывает `job_id`] → process-local множество в работе; имя файла в
|
||||
горячей папке = `job_id`, повторный `put` перезаписывает тот же файл.
|
||||
- [Публичные образы PostgreSQL/NATS/MinIO отличаются от банковских] → версии зафиксированы по ответам (16 / 2.10 без JS).
|
||||
- [Шаблон инициализирует nkeys при импорте `app.py`] → задача сервисам: пустые `NATS_SEED/NATS_JWT` = соединение без
|
||||
учётных данных (dev); при заданных — как в шаблоне.
|
||||
- [Копии контракта расходятся] → тест идентичности в каждом сервисе; PR в docs без PR в сервисы — красный CI сервисов.
|
||||
- [Эмулятор отвечает только для файлов, у которых есть пара `aep_N.json` в фикстурах (сейчас две)] → для остальных имён
|
||||
эмулятор возвращает ответ с `error.code = INVALID_DOCUMENT` в формате Content AI — это тоже проверяемый сценарий.
|
||||
- [Копии контракта расходятся] → тест идентичности в `make check` каждого сервиса.
|
||||
- [Эмулятор отвечает только для файлов, у которых есть пара `aep_N.json` (сейчас две)] → для остальных имён —
|
||||
ответ с `error.code = INVALID_DOCUMENT` в формате Content AI; это тоже проверяемый сценарий.
|
||||
- [Нет CI — проверки можно забыть] → `make check-full` в DoD каждой задачи и в описании PR; ревьюер требует вывод.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Нет данных для миграции. Порядок внедрения: контракт → compose инфраструктуры → arm-core → сервисы → compose профиль
|
||||
`services` → CI → документы. Откат — удаление ветки; `docs/contracts/recognition-result.schema.json` остаётся совместимым
|
||||
с текущим черновиком (добавлен только код ошибки).
|
||||
`services` → проверки качества → документы. Откат — удаление ветки; `docs/contracts/recognition-result.schema.json`
|
||||
остаётся совместимым с текущим черновиком (добавлен только код ошибки).
|
||||
|
||||
## Open Questions (к человеку; не меняют структуру change'а, но нужны до первого деплоя)
|
||||
## Open Questions
|
||||
|
||||
1. Версия Python в базовом образе платформы (в шаблоне нет пина; локальный venv — 3.13) и версия Node для `web/`
|
||||
(нет `engines`). Пока compose/CI берут `python:3.12-slim` и `node:22`.
|
||||
2. Доступ к Nexus банка (`raisa_fastapi_protected_api`, `raisa-ui`, `raisa-fastapi-protected-web`) из dev-среды и CI:
|
||||
прокси, вендоренные wheel/tgz, или оставить условные импорты до деплоя.
|
||||
3. Gitea Actions на `git.aepif.ru`: включены ли, есть ли раннер с Docker (нужны `services:` и `docker build`), реестр
|
||||
образов для push (адрес, учётные данные как секреты).
|
||||
4. Доступ CI к `RSHB/arm-ka-fixtures`: токен/deploy key только на чтение (секрет `FIXTURES_TOKEN`).
|
||||
5. NATS банка: включён ли JetStream; кто создаёт stream `RECOGNITION` и consumer'ы — мы при старте или платформа;
|
||||
параметры nkeys/JWT для трёх сервисов.
|
||||
6. PostgreSQL банка: версия (нужна ≥ 15 ради `NULLS NOT DISTINCT`), право на `CREATE SCHEMA dkb` и `CREATE TYPE`.
|
||||
7. JWT РАИСА: алгоритм и источник ключей (JWKS URL / публичный ключ), имена claim'ов (email, subject), issuer/audience —
|
||||
для `RaisaVerifier`; до ответа работает только dev-эмитент.
|
||||
Отвечено 03.10.2026: Python 3.11; Nexus недоступен до прода; CI в Gitea нет, пайплайн платформы не трогаем; JetStream нет;
|
||||
PostgreSQL 16, схемы создавать можно. Остаются (не блокируют старт):
|
||||
|
||||
1. Версия Node для `web/` (пока `node:22`).
|
||||
2. JWT РАИСА: алгоритм, источник ключей, имена claim'ов, issuer/audience — нужны к первому деплою; до этого dev-эмитент.
|
||||
3. Имена входящего/исходящего каталогов и путь SMB на стенде ИС Сканирование — к Срезу 3.
|
||||
4. Появится ли JetStream на платформе позже — влияет только на publisher/subscriber (D-3).
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
@ -104,12 +134,13 @@
|
||||
|---|---|---|---|---|
|
||||
| 1 | 2026-10-03 | D-1: контракт вендорится копией, общего пакета нет | нет внутреннего PyPI; три потребителя | полдня на переход к пакету |
|
||||
| 2 | 2026-10-03 | D-2: `group_schema` убран из конверта результата | одна истина в файле результата | правка одной таблицы в docs и одной pydantic-модели |
|
||||
| 3 | 2026-10-03 | D-3/D-4: именование `Nats-Msg-Id`, один stream, параметры consumer'ов | DECISION-4, OQ-15 | переименование констант в трёх сервисах |
|
||||
| 4 | 2026-10-03 | D-5: один бакет `arm-ka`, роли как префиксы | шаблон команды (`MINIO_BUCKET`) | `s3_object.bucket` уже хранится отдельно — смена на несколько бакетов не ломает модель |
|
||||
| 3 | 2026-10-03 | D-3/D-4: без JetStream — ack `processing`, повторная публикация ядром (30 с, 3 раза), queue group'ы, дедуп по `result_msg_id` | ответ пользователя «JetStream нет»; DECISION-4 требует не трогать БД из сервисов | при появлении JetStream — замена publisher/subscriber |
|
||||
| 4 | 2026-10-03 | D-5: один бакет `arm-ka`, роли как префиксы | шаблон команды (`MINIO_BUCKET`) | `s3_object.bucket` хранится отдельно — смена на несколько бакетов не ломает модель |
|
||||
| 5 | 2026-10-03 | D-6: эмулятор ИС Сканирование живёт в `scan-gateway` | общие соглашения горячих папок | перенос каталога |
|
||||
| 6 | 2026-10-03 | D-7: compose на уровне workspace в `docs/dev/`, `Dockerfile` разыгнорирован в сервисах | шаблон игнорирует Dockerfile, платформа собирает сама | вернуть строку в `.gitignore`, перенести Dockerfile в `docs/dev/` |
|
||||
| 7 | 2026-10-03 | D-8: CI как Gitea Actions (GitHub-синтаксис) | Gitea у заказчика; синтаксис переносим | переписать YAML под другой раннер |
|
||||
| 8 | 2026-10-03 | В контракт v1.0 добавлен код ошибки `NOT_IMPLEMENTED` | заглушки Среза 0 и будущие неподдерживаемые форматы не должны маскироваться под `UNSUPPORTED_FORMAT` | удалить одно значение enum в 1.1 |
|
||||
| 9 | 2026-10-03 | Справочники продукта для seed (план счетов, правила элиминации, права) — в `arm-core/api/seed/`, не в `fixtures/directories/` | это данные продукта, нужны в проде; `fixtures` — приватные тестовые данные | перенос файлов и правка §6 `06-test-data.md` |
|
||||
| 10 | 2026-10-03 | Версии образов dev: PostgreSQL 15, NATS 2.10, MinIO стабильный релиз, `python:3.12-slim`, `node:22` | минимумы из CLAUDE.md; версии банка неизвестны (Open Questions 1, 6) | смена тега в compose/CI |
|
||||
| 11 | 2026-10-03 | Worktree среза собираются в зеркальный workspace `.wt/<срез>/` с симлинками | относительные пути `../docs`, `../fixtures` и хуки должны работать в ветке | переместить worktree |
|
||||
| 6 | 2026-10-03 | D-7: compose в `docs/dev/`, `Dockerfile` разыгнорирован в сервисах | шаблон игнорирует Dockerfile, платформа собирает сама | вернуть строку в `.gitignore` |
|
||||
| 7 | 2026-10-03 | D-8: проверки качества — `make check` / `make check-full`, конфигураций CI нет | CI в Gitea нет, пайплайн платформы не наш | добавить YAML под нужный раннер, команды уже есть |
|
||||
| 8 | 2026-10-03 | В контракт v1.0 добавлен код ошибки `NOT_IMPLEMENTED` и статус конверта `processing` (ack) | заглушки Среза 0; доставка без JetStream | удалить значения enum в 1.1 |
|
||||
| 9 | 2026-10-03 | Справочники продукта для seed — в `arm-core/api/seed/`, не в `fixtures/directories/` | данные продукта нужны в проде; `fixtures` — приватные тестовые данные | перенос файлов и правка §6 `06-test-data.md` |
|
||||
| 10 | 2026-10-03 | D-9: `python:3.11-slim`, PostgreSQL 16, NATS 2.10 без JS, `node:22` до ответа | ответы пользователя; Node неизвестен | смена тега в compose/Dockerfile |
|
||||
| 11 | 2026-10-03 | Worktree среза — зеркальный workspace `.wt/<срез>/` с симлинками | относительные пути `../docs`, `../fixtures` и хуки должны работать в ветке | переместить worktree |
|
||||
| 12 | 2026-10-03 | D-10: `raisa_*` фильтруются при dev-установке, npm-пакеты `raisa-*` → `optionalDependencies` | Nexus недоступен до прода | вернуть две строки в `dependencies` |
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Proposal: Срез 0 — сквозной скелет (контракт v1.0, окружение разработки, CI-шаблон)
|
||||
# Proposal: Срез 0 — сквозной скелет (контракт v1.0, окружение разработки, проверки качества)
|
||||
|
||||
**Срез:** 0 «Скелет» (`docs/context/09-delivery-plan.md`). **Репозиторий:** arm-ka-docs (сквозная часть).
|
||||
**Issue:** RSHB/arm-ka-docs#1. **Дочерние change'и:** `arm-core/openspec/changes/slice-0-skeleton` (arm-core#1),
|
||||
@ -9,9 +9,10 @@
|
||||
|
||||
Три сервисных репозитория развёрнуты из шаблона команды, но между ними ещё нет ничего общего: контракт результата
|
||||
распознавания помечен «черновик», сообщения NATS описаны только таблицей в `03-architecture.md`, локально поднять
|
||||
PostgreSQL + NATS + MinIO + ИС Сканирование нечем, CI-конфигурации у шаблона нет. Без этого Срез 1 (справочники) и
|
||||
Срез 2 (парсер) нельзя начать параллельно в трёх репозиториях: каждый сервис придумает свой формат сообщений и своё
|
||||
окружение. Срез 0 фиксирует общие точки один раз и проверяет их end-to-end на пустых обработчиках.
|
||||
PostgreSQL + NATS + MinIO + ИС Сканирование нечем, единого набора проверок перед PR нет. Без этого Срез 1 (справочники)
|
||||
и Срез 2 (парсер) нельзя начать параллельно в трёх репозиториях: каждый сервис придумает свой формат сообщений и своё
|
||||
окружение. Срез 0 фиксирует общие точки один раз и проверяет их end-to-end на пустых обработчиках — на той платформе,
|
||||
которая есть: NATS без JetStream, PostgreSQL 16, Python 3.11, без доступа к Nexus и без CI в Gitea.
|
||||
|
||||
## What Changes
|
||||
|
||||
@ -21,52 +22,57 @@ PostgreSQL + NATS + MinIO + ИС Сканирование нечем, CI-кон
|
||||
`statementDocument`) не меняется — она уточняется в Срезе 2/3 версией 1.x (additive) или 2.0 (breaking).
|
||||
- **Новое:** `recognition-request.schema.json` — тело сообщения `recognition.{tabular|image}.request`
|
||||
(`job_id`, `file_id`, `pipeline`, `file_type`, `original_name`, `s3 {bucket, key}`, `reply_deadline`, `attempt`).
|
||||
- **Новое:** `recognition-result-message.schema.json` — тело сообщения `recognition.result` (конверт: `job_id`,
|
||||
`file_id`, `status`, `error`, `service`, `result_s3 {bucket, key}`, `result_schema_version`, `raw_s3_key`, `metrics`).
|
||||
Поле `group_schema` в конверт **не входит** — ядро берёт его из `documents[].group_schema` файла результата
|
||||
(правка таблицы в `03-architecture.md`).
|
||||
- Правила заголовков NATS: `Nats-Msg-Id` запроса = `<job_id>:<attempt>`, результата = `result:<job_id>:<attempt>`.
|
||||
- Примеры сообщений и результата — `docs/contracts/examples/` (валидируются в CI сервисов).
|
||||
- **Новое:** `recognition-result-message.schema.json` — тело сообщения `recognition.result`: конверт со `status`
|
||||
`processing` (подтверждение взятия в работу — замена ack брокера, которого без JetStream нет) | `done` | `needs_check`
|
||||
| `error`; поля `job_id`, `file_id`, `error`, `service`, `result_s3 {bucket, key}`, `result_schema_version`,
|
||||
`raw_s3_key`, `metrics`. Поле `group_schema` в конверт **не входит** — ядро берёт его из `documents[].group_schema`
|
||||
файла результата (правка таблицы в `03-architecture.md`).
|
||||
- Заголовки NATS: `Nats-Msg-Id` запроса = `<job_id>:<attempt>`, результата = `result:<job_id>:<attempt>`, ack —
|
||||
`ack:<job_id>:<attempt>`; без JetStream они нужны для трассировки, дедуп результатов — `recognition_job.result_msg_id`.
|
||||
- Примеры сообщений и результата — `docs/contracts/examples/` (валидируются тестами сервисов).
|
||||
- **Окружение разработки** `docs/dev/`:
|
||||
- `docker-compose.yml` workspace-уровня: PostgreSQL 15, NATS 2.10 с JetStream, MinIO (+ init-контейнер: бакет
|
||||
`arm-ka`), эмулятор ИС Сканирование (образ `scan-gateway`, команда эмулятора, общая папка горячих каталогов),
|
||||
сервисы `arm-core`, `tabular-parser`, `scan-gateway` (профиль `services`; сборка из соседних каталогов workspace).
|
||||
- `.env.example` c переменными шаблона (`OPENBAO__*`, `DEBUG`) и флагами разработки (`AUTH_DEV_ISSUER`, `SCAN_EMULATOR`).
|
||||
- `README.md`: как поднять инфраструктуру, как запустить сервисы из venv против compose, как прогнать интеграционные тесты.
|
||||
- **CI-шаблон** `docs/dev/ci/`: workflow Gitea Actions для Python-сервиса (ruff, mypy, pytest с сервисами PostgreSQL/NATS/MinIO,
|
||||
проверка идентичности вендоренного контракта с `docs/contracts`, сборка образа; для arm-core — плюс миграции с нуля и
|
||||
назад, `check_dbml_vs_orm.py`, job для `web/`: biome, tsc, rsbuild build). Checkout `arm-ka-fixtures` в `../fixtures`
|
||||
(или `ARM_KA_FIXTURES`) — через секрет с правом чтения.
|
||||
- `docker-compose.yml` workspace-уровня: PostgreSQL 16, NATS 2.10 **без JetStream** (паритет с платформой), MinIO
|
||||
(+ init-контейнер: бакет `arm-ka`), эмулятор ИС Сканирование (образ `scan-gateway`, команда эмулятора, общая папка
|
||||
горячих каталогов), сервисы `arm-core`, `tabular-parser`, `scan-gateway` (профиль `services`; сборка из соседних
|
||||
каталогов workspace, `python:3.11-slim`).
|
||||
- `.env.example` c переменными шаблона (`OPENBAO__*`, `DEBUG`) и флагами разработки (`AUTH_DEV_ISSUER`, `SCAN_EMULATOR`,
|
||||
`NATS_ENABLED`).
|
||||
- `README.md`: как поднять инфраструктуру, как запустить сервисы из venv против compose без Nexus, как прогнать
|
||||
интеграционные тесты.
|
||||
- **Проверки качества** `docs/dev/checks.md`: одинаковые цели `make check` (ruff, mypy, pytest без Docker, идентичность
|
||||
контракта) и `make check-full` (+ интеграционные тесты против compose; для arm-core — цикл миграций и
|
||||
`check_dbml_vs_orm.py`) в каждом репозитории; `make check-full` обязателен перед PR. Конфигураций CI в репозиториях
|
||||
нет: в Gitea CI не будет, пайплайн платформы — вне нашей зоны.
|
||||
- **Документы контекста:** `03-architecture.md` — раздел «Соглашения шаблона команды», раскладка `api/src/`, схема `dkb`,
|
||||
один бакет S3, конверт результата без `group_schema`; `00-workspace-layout.md` — worktree-workspace `.wt/<срез>/`;
|
||||
`06-test-data.md` §6 — справочники для seed продукта (план счетов, правила элиминации, права) живут в `arm-core/api/seed/`,
|
||||
в `fixtures/directories/` остаются только тестовые справочники (ЮЛ и ГК группы «Русский бисквит», Срез 1);
|
||||
`09-delivery-plan.md` — решение по пакету контрактов (без общего пакета, см. design); `07-open-questions.md` — новые OQ.
|
||||
один бакет S3, обмен через NATS без JetStream (ack, повторная публикация), проверки качества вместо CI; `00-workspace-layout.md`
|
||||
— worktree-workspace `.wt/<срез>/`, фикстуры для проверок; `06-test-data.md` §6 — справочники для seed продукта живут в
|
||||
`arm-core/api/seed/`; `09-delivery-plan.md` — Срез 0 по факту; `07-open-questions.md` — новые OQ.
|
||||
- **Сверка макета** (задача B kickoff): `10-user-journey.md`, `mockup/INDEX.md`, новые снимки `mockup/screens/`,
|
||||
`scripts/mockup_crawl_extra.py`; расхождения — в `07-open-questions.md`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `recognition-contract`: контракт обмена между ядром и сервисами распознавания — тела сообщений NATS, файл результата
|
||||
в S3, правила версионирования и идемпотентности.
|
||||
- `dev-environment`: воспроизводимое локальное окружение workspace (PostgreSQL, NATS JetStream, MinIO, эмулятор ИС
|
||||
Сканирование, три сервиса) с одним `docker compose up`.
|
||||
- `ci-template`: обязательный набор проверок CI для сервисных репозиториев и порядок их выполнения.
|
||||
- `recognition-contract`: контракт обмена между ядром и сервисами распознавания — тела сообщений NATS (запрос, ack,
|
||||
результат), файл результата в S3, правила версионирования и идемпотентности без JetStream.
|
||||
- `dev-environment`: воспроизводимое локальное окружение workspace (PostgreSQL, NATS, MinIO, эмулятор ИС Сканирование,
|
||||
три сервиса) с одним `docker compose up`, без доступа к Nexus.
|
||||
- `quality-checks`: единый набор проверок качества перед PR для сервисных репозиториев (`make check`, `make check-full`).
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- openspec/specs пуст — изменяемых capability нет -->
|
||||
|
||||
## Impact
|
||||
|
||||
- Читают контракт: `arm-core` (outbox публикует запрос, consumer читает конверт результата и файл результата),
|
||||
`tabular-parser` и `scan-gateway` (читают запрос, пишут результат и конверт). Каждый сервис вендорит копию схем в
|
||||
`api/contracts/` и проверяет идентичность с `docs/contracts` в CI.
|
||||
- Читают контракт: `arm-core` (outbox публикует запрос, consumer читает ack и конверт результата и файл результата),
|
||||
`tabular-parser` и `scan-gateway` (читают запрос, пишут ack, результат и конверт). Каждый сервис вендорит копию схем
|
||||
в `api/contracts/` и проверяет идентичность с `docs/contracts` в `make check`.
|
||||
- Таблицы DBML v5.13, которые опираются на контракт: `recognition_job` (`request_msg_id`, `result_msg_id`,
|
||||
`schema_version`, `result_schema_version`, `service_name/version`, `result_s3_object_id`, `metrics`, `group_schema`,
|
||||
`error_code`), `s3_object` (`bucket_role`, `bucket`, `key`, `created_by`), `stored_file` (`file_type`). Новых полей нет.
|
||||
- Внешние зависимости: Docker/Compose у разработчика; Gitea Actions с раннером, умеющим `services:` и `docker build`
|
||||
(вопрос к человеку — см. design «Open Questions»).
|
||||
`schema_version`, `result_schema_version`, `service_name/version`, `status` включая `processing` и `dlq`, `started_at`,
|
||||
`result_s3_object_id`, `metrics`, `group_schema`, `error_code`), `s3_object` (`bucket_role`, `bucket`, `key`,
|
||||
`created_by`), `stored_file` (`file_type`). Новых полей нет.
|
||||
- Внешние зависимости: Docker/Compose у разработчика; публичные PyPI/npm (пакеты `raisa_*` недоступны — условные импорты
|
||||
и `optionalDependencies`, см. design D-10).
|
||||
|
||||
## Non-goals
|
||||
|
||||
@ -75,4 +81,5 @@ PostgreSQL + NATS + MinIO + ИС Сканирование нечем, CI-кон
|
||||
- Контракт `ex-payload-v1.schema.json` (Срез 7).
|
||||
- Production-значения инфраструктуры банка (адреса NATS/S3/OpenBao, nkeys) — compose только для разработки.
|
||||
- Публикация общего python-пакета контрактов (решено: не делать, см. design D-1).
|
||||
- Конфигурации CI для Gitea или платформы банка.
|
||||
- Какие-либо экраны фронта.
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
# Spec Delta: ci-template
|
||||
|
||||
## Purpose
|
||||
|
||||
Единый шаблон CI (Gitea Actions) для сервисных репозиториев АРМ КА: какие проверки обязательны, в каком порядке и с
|
||||
какими внешними ресурсами они выполняются, чтобы PR в любой из трёх репозиториев проверялся одинаково.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Обязательные проверки Python-сервиса
|
||||
Шаблон `docs/dev/ci/python-service.yml` SHALL выполнять на каждый push в ветку и PR в `main`: установку зависимостей из
|
||||
`api/requirements.txt` и `api/requirements-dev.txt`; `ruff check` и `ruff format --check` по `api/`; `mypy api/src`;
|
||||
`pytest` из `api/` с сервисами PostgreSQL 15, NATS 2.10 (JetStream) и MinIO, доступными по переменным `OPENBAO__*`;
|
||||
тест идентичности вендоренного контракта с `docs/contracts`; сборку образа `docker build` без push.
|
||||
|
||||
#### Scenario: PR с нарушением формата
|
||||
- **WHEN** в PR есть файл, не отформатированный `ruff format`
|
||||
- **THEN** job `lint` падает до запуска тестов, остальные job'ы не стартуют
|
||||
|
||||
#### Scenario: Зелёный PR
|
||||
- **WHEN** все проверки проходят
|
||||
- **THEN** статус PR в Gitea — success, артефакт отчёта pytest (junit) прикреплён к run
|
||||
|
||||
### Requirement: Дополнительные проверки arm-core
|
||||
Для `arm-core` шаблон SHALL дополнительно: применить миграции с нуля (`alembic upgrade head`) к пустой БД, откатить
|
||||
(`alembic downgrade base`) и применить снова; запустить `api/scripts/check_dbml_vs_orm.py` против
|
||||
`docs/model/data-model-v5.13.dbml`; выполнить job `web`: `npm ci`, `npx biome check src`, `npx tsc --noEmit`, `npm run build`.
|
||||
|
||||
#### Scenario: Расхождение ORM и DBML
|
||||
- **WHEN** в ORM добавлена колонка, которой нет в DBML
|
||||
- **THEN** шаг `check_dbml_vs_orm` падает с перечнем расхождений, PR красный
|
||||
|
||||
#### Scenario: Миграция без отката
|
||||
- **WHEN** `downgrade` ревизии не удаляет созданный ею объект
|
||||
- **THEN** шаг «upgrade → downgrade → upgrade» падает
|
||||
|
||||
### Requirement: Доступ к соседним репозиториям
|
||||
Шаблон SHALL получать `arm-ka-docs` (для контракта и DBML) и `arm-ka-fixtures` (для тестов на реальных данных)
|
||||
checkout'ом в `../docs` и `../fixtures` c токеном только на чтение из секретов репозитория (`DOCS_TOKEN`,
|
||||
`FIXTURES_TOKEN`); при отсутствии секрета фикстурные тесты SHALL пропускаться с явным предупреждением, а не падать.
|
||||
|
||||
#### Scenario: Нет доступа к фикстурам
|
||||
- **WHEN** секрет `FIXTURES_TOKEN` не задан
|
||||
- **THEN** тесты с маркером `fixtures` помечены skipped с причиной «ARM_KA_FIXTURES недоступен», остальные выполняются
|
||||
|
||||
### Requirement: Шаблон параметризуется именем сервиса
|
||||
Копия шаблона в `<repo>/.gitea/workflows/ci.yml` SHALL отличаться от `docs/dev/ci/python-service.yml` только блоком
|
||||
`env:` (имя сервиса, порт, включение шагов arm-core); расхождение структуры шагов считается ошибкой ревью.
|
||||
|
||||
#### Scenario: Сверка с шаблоном
|
||||
- **WHEN** ревьюер сравнивает `ci.yml` сервиса с шаблоном (`diff` без блока `env:`)
|
||||
- **THEN** различий нет
|
||||
@ -2,21 +2,21 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
Локальное окружение разработки всего workspace одной командой: инфраструктура (PostgreSQL, NATS JetStream, MinIO),
|
||||
Локальное окружение разработки всего workspace одной командой: инфраструктура (PostgreSQL 16, NATS без JetStream, MinIO),
|
||||
эмулятор внешней ИС Сканирование и три сервиса системы, собранные из соседних каталогов workspace.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Инфраструктура поднимается одной командой
|
||||
`docker compose -f docs/dev/docker-compose.yml up -d` без профилей SHALL поднять PostgreSQL 15 (БД `armka`,
|
||||
пользователь `armka`, порт 5432), NATS 2.10 с включённым JetStream (порты 4222, мониторинг 8222) и MinIO
|
||||
`docker compose -f docs/dev/docker-compose.yml up -d` без профилей SHALL поднять PostgreSQL 16 (БД `armka`,
|
||||
пользователь `armka`, порт 5432), NATS 2.10 **без JetStream** (как на платформе; порты 4222, мониторинг 8222) и MinIO
|
||||
(API 9000, консоль 9001) с уже созданным бакетом `arm-ka`. Все учётные данные — в `docs/dev/.env.example`, копируемом
|
||||
в `docs/dev/.env`.
|
||||
|
||||
#### Scenario: Чистый старт
|
||||
- **WHEN** разработчик выполняет `cp .env.example .env && docker compose up -d` в `docs/dev/` на машине без томов
|
||||
- **THEN** в течение 60 с все три контейнера проходят healthcheck, `psql` подключается к `armka`, `nats stream ls`
|
||||
отвечает (JetStream включён), `mc ls local/arm-ka` возвращает пустой бакет
|
||||
- **THEN** в течение 60 с все три контейнера проходят healthcheck, `psql` подключается к `armka`, `curl :8222/varz`
|
||||
отвечает и `jetstream` в нём отсутствует/выключен, `mc ls local/arm-ka` возвращает пустой бакет
|
||||
|
||||
### Requirement: Профиль сервисов собирает приложения из workspace
|
||||
Профиль `services` SHALL добавить контейнеры `arm-core`, `tabular-parser`, `scan-gateway` и `scan-emulator`, собранные
|
||||
@ -27,12 +27,12 @@ workspace `.wt/<срез>/`). Сервисы читают настройки ч
|
||||
#### Scenario: Сервисы стартуют против инфраструктуры compose
|
||||
- **WHEN** выполняется `docker compose --profile services up -d --build`
|
||||
- **THEN** `GET http://localhost:8000/healthcheck` (arm-core), `:8001/healthcheck` (tabular-parser),
|
||||
`:8002/healthcheck` (scan-gateway) отвечают 200 с `version`; в логах нет ошибок подключения к NATS/S3; stream
|
||||
`RECOGNITION` существует
|
||||
`:8002/healthcheck` (scan-gateway) отвечают 200 с `version` и `nats = ok`; в логах нет ошибок подключения к NATS/S3
|
||||
|
||||
#### Scenario: Запуск из venv против compose
|
||||
- **WHEN** разработчик поднял только инфраструктуру и запускает `python run_server.py` из `api/` любого сервиса с
|
||||
`.env`, скопированным из `docs/dev/.env.example`
|
||||
#### Scenario: Запуск из venv против compose без Nexus
|
||||
- **WHEN** разработчик поднял только инфраструктуру, создал venv командой `make venv` (ставит `requirements.txt` без
|
||||
строк `raisa_*`) и запускает `python run_server.py` из `api/` любого сервиса с `.env`, скопированным из
|
||||
`docs/dev/.env.example`
|
||||
- **THEN** сервис подключается к тем же PostgreSQL/NATS/MinIO по `localhost` и проходит healthcheck
|
||||
|
||||
### Requirement: Эмулятор ИС Сканирование воспроизводит горячие папки
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
# Spec Delta: quality-checks
|
||||
|
||||
## Purpose
|
||||
|
||||
Единый набор проверок качества для сервисных репозиториев АРМ КА, выполняемый локально одной командой перед каждым PR:
|
||||
какие проверки обязательны, в каком порядке и с какими внешними ресурсами. Конфигураций CI в репозиториях нет — в Gitea
|
||||
CI не используется, пайплайн платформы банка вне зоны проекта.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: `make check` — быстрые проверки без Docker
|
||||
Каждый сервисный репозиторий SHALL предоставлять цель `make check`, выполняющую из `api/`: `ruff check .`,
|
||||
`ruff format --check .`, `mypy src`, `pytest -m "not integration"`, тест идентичности вендоренного контракта с
|
||||
`../docs/contracts`. Цель SHALL завершаться ненулевым кодом при первой упавшей проверке и MUST NOT требовать запущенного
|
||||
Docker, PostgreSQL или NATS.
|
||||
|
||||
#### Scenario: Файл не отформатирован
|
||||
- **WHEN** в репозитории есть файл, не отформатированный `ruff format`
|
||||
- **THEN** `make check` завершается с кодом 1 на шаге `ruff format --check`, следующие шаги не выполняются
|
||||
|
||||
#### Scenario: Чистый репозиторий
|
||||
- **WHEN** все проверки проходят
|
||||
- **THEN** `make check` завершается с кодом 0 за время, не превышающее нескольких минут, без обращения к сети
|
||||
|
||||
### Requirement: `make check-full` — полная проверка против compose
|
||||
Каждый сервисный репозиторий SHALL предоставлять цель `make check-full`: `make check` плюс `pytest -m integration` против
|
||||
инфраструктуры `docs/dev/docker-compose.yml` (PostgreSQL 16, NATS, MinIO, для `scan-gateway` — эмулятор). Для `arm-core`
|
||||
цель SHALL дополнительно выполнять «`alembic upgrade head` → `alembic downgrade base` → `alembic upgrade head`» на чистой
|
||||
БД и `python scripts/check_dbml_vs_orm.py` против `../docs/model/data-model-v5.13.dbml`. `make check-full` SHALL быть
|
||||
выполнен перед созданием PR, его итог вписывается в описание PR.
|
||||
|
||||
#### Scenario: Расхождение ORM и DBML
|
||||
- **WHEN** в ORM добавлена колонка, которой нет в DBML
|
||||
- **THEN** шаг `check_dbml_vs_orm` падает с перечнем расхождений, `make check-full` завершается с кодом 1
|
||||
|
||||
#### Scenario: Миграция без отката
|
||||
- **WHEN** `downgrade` ревизии не удаляет созданный ею объект
|
||||
- **THEN** шаг «upgrade → downgrade → upgrade» падает
|
||||
|
||||
### Requirement: Фикстуры и соседние репозитории — опциональны с предупреждением
|
||||
Проверки SHALL брать `docs` и `fixtures` из соседних каталогов workspace (`../docs`, `../fixtures`) или из переменных
|
||||
`ARM_KA_DOCS`, `ARM_KA_FIXTURES`. При отсутствии фикстур тесты с маркером `fixtures` SHALL пропускаться с явной причиной;
|
||||
при отсутствии docs тест идентичности контракта SHALL пропускаться с предупреждением. Отсутствие соседей MUST NOT делать
|
||||
`make check` красным.
|
||||
|
||||
#### Scenario: Нет фикстур
|
||||
- **WHEN** `../fixtures` отсутствует и `ARM_KA_FIXTURES` не задан
|
||||
- **THEN** тесты с маркером `fixtures` помечены skipped с причиной «ARM_KA_FIXTURES недоступен», остальные выполняются
|
||||
|
||||
### Requirement: Одинаковый набор целей во всех репозиториях
|
||||
`Makefile` каждого сервисного репозитория SHALL содержать цели `up`, `test`, `test-integration`, `lint`, `check`,
|
||||
`check-full` с одинаковой семантикой (описана в `docs/dev/checks.md`); репозиторий MAY добавлять свои цели (`migrate`,
|
||||
`seed`, `emulator`), но MUST NOT менять смысл общих.
|
||||
|
||||
#### Scenario: Сверка с описанием
|
||||
- **WHEN** ревьюер сравнивает список общих целей `Makefile` сервиса с `docs/dev/checks.md`
|
||||
- **THEN** все общие цели присутствуют и запускают описанные команды
|
||||
@ -24,12 +24,34 @@
|
||||
- **WHEN** сервис получает запрос, не проходящий схему (нет `s3.key`, неизвестный `file_type`)
|
||||
- **THEN** сервис публикует результат со `status = error`, `error.code = SCHEMA_MISMATCH` и не падает
|
||||
|
||||
### Requirement: Подтверждение взятия в работу (ack без JetStream)
|
||||
Получив запрос, сервис SHALL немедленно, до чтения файла из S3, опубликовать в `recognition.result` конверт со
|
||||
`status = processing`, `job_id`, `file_id`, `service {name, version}` и заголовком `Nats-Msg-Id = ack:<job_id>:<attempt>`.
|
||||
Ядро SHALL перевести задание `published → processing` и записать `started_at`, `service_name`, `service_version`. Если
|
||||
ack не получен за `ACK_TIMEOUT_SECONDS` (по умолчанию 30), ядро SHALL опубликовать запрос повторно с тем же
|
||||
`Nats-Msg-Id` и тем же `attempt`, не более `OUTBOX_MAX_REPUBLISH` (по умолчанию 3) раз, после чего перевести задание в `dlq`.
|
||||
|
||||
#### Scenario: Сервис жив
|
||||
- **WHEN** запрос опубликован и сервис подписан
|
||||
- **THEN** в течение `ACK_TIMEOUT_SECONDS` в `recognition.result` появляется конверт `processing`, задание получает
|
||||
`status = processing` и `started_at`
|
||||
|
||||
#### Scenario: Сервис недоступен
|
||||
- **WHEN** ни один инстанс сервиса не подписан на subject запроса
|
||||
- **THEN** ядро публикует запрос повторно каждые `ACK_TIMEOUT_SECONDS`; после `OUTBOX_MAX_REPUBLISH` повторов задание
|
||||
получает `status = dlq`, `request_msg_id` не меняется
|
||||
|
||||
#### Scenario: Повторный запрос пришёл в работающий сервис
|
||||
- **WHEN** сервис получает запрос с `job_id`, который уже обрабатывается или обработан этим процессом
|
||||
- **THEN** сервис публикует ack повторно и не начинает вторую обработку
|
||||
|
||||
### Requirement: Сообщение результата распознавания
|
||||
Тело сообщения на subject `recognition.result` SHALL соответствовать `docs/contracts/recognition-result-message.schema.json`
|
||||
v1.0: обязательные поля `schema_version` (`"1.0"`), `job_id`, `file_id`, `status` (`done` | `needs_check` | `error`),
|
||||
`service {name, version}`; при `status ≠ error` обязательны `result_s3 {bucket, key}` и `result_schema_version`;
|
||||
при `status = error` обязателен `error {code, message}`; опциональны `raw_s3_key` (только `scan-gateway`) и `metrics`.
|
||||
Поле `group_schema` в конверт NOT включается — источник `recognition_job.group_schema` — файл результата.
|
||||
v1.0: обязательные поля `schema_version` (`"1.0"`), `job_id`, `file_id`, `status` (`processing` | `done` | `needs_check` |
|
||||
`error`), `service {name, version}`; при `status ∈ {done, needs_check}` обязательны `result_s3 {bucket, key}` и
|
||||
`result_schema_version`; при `status = error` обязателен `error {code, message}`; опциональны `raw_s3_key`
|
||||
(только `scan-gateway`) и `metrics`. Поле `group_schema` в конверт NOT включается — источник `recognition_job.group_schema`
|
||||
— файл результата.
|
||||
|
||||
#### Scenario: Успешный результат
|
||||
- **WHEN** сервис завершил обработку файла
|
||||
@ -63,10 +85,20 @@ v1.0: обязательные поля `schema_version` (`"1.0"`), `job_id`, `f
|
||||
- **THEN** публикуется результат `status = error`, `error.code = SCHEMA_MISMATCH`, запрос подтверждается (ack), чтобы не
|
||||
зациклить доставку
|
||||
|
||||
### Requirement: Идемпотентность результатов без брокера
|
||||
Публикующий SHALL ставить заголовок `Nats-Msg-Id` на каждое сообщение (запрос `<job_id>:<attempt>`, ack
|
||||
`ack:<job_id>:<attempt>`, результат `result:<job_id>:<attempt>`). Поскольку серверного дедупа нет, ядро SHALL считать
|
||||
результат дубликатом, если `recognition_job.result_msg_id` уже равен `Nats-Msg-Id` сообщения, и MUST NOT менять задание
|
||||
повторно; потерянный результат SHALL обнаруживаться по `reply_deadline` (`timeout`).
|
||||
|
||||
#### Scenario: Результат доставлен дважды
|
||||
- **WHEN** сервис опубликовал один и тот же результат два раза (повтор публикации после сетевой ошибки)
|
||||
- **THEN** задание изменено один раз, второе сообщение проигнорировано
|
||||
|
||||
### Requirement: Копия контракта в сервисе идентична источнику
|
||||
Каждый сервис, читающий или пишущий контракт, SHALL хранить копию файлов схем в `api/contracts/` и проверять в CI их
|
||||
байтовую идентичность файлам `docs/contracts/` (checkout `arm-ka-docs` или путь `ARM_KA_DOCS`).
|
||||
Каждый сервис, читающий или пишущий контракт, SHALL хранить копию файлов схем в `api/contracts/` и проверять в `make check`
|
||||
их байтовую идентичность файлам `docs/contracts/` (`../docs` или путь `ARM_KA_DOCS`; при недоступности — пропуск с предупреждением).
|
||||
|
||||
#### Scenario: Копия устарела
|
||||
- **WHEN** в `docs/contracts/` изменена схема, а копия в сервисе — нет
|
||||
- **THEN** тест идентичности в CI сервиса падает с указанием имени файла
|
||||
- **THEN** тест идентичности в `make check` сервиса падает с указанием имени файла
|
||||
|
||||
@ -10,59 +10,65 @@
|
||||
(метасхема 2020-12 валидна)
|
||||
- [ ] 1.2 [docs] Новый `docs/contracts/recognition-request.schema.json` по spec `recognition-contract` (поля, enum,
|
||||
форматы uuid/date-time); проверить метасхемой
|
||||
- [ ] 1.3 [docs] Новый `docs/contracts/recognition-result-message.schema.json` (конверт результата, условные
|
||||
обязательные поля через `if/then`); проверить метасхемой
|
||||
- [ ] 1.3 [docs] Новый `docs/contracts/recognition-result-message.schema.json` (конверт: `status` ∈ processing | done |
|
||||
needs_check | error; условные обязательные поля через `if/then`); проверить метасхемой
|
||||
- [ ] 1.4 [docs] Примеры `docs/contracts/examples/`: `request-tabular.json`, `request-image.json`,
|
||||
`result-message-done.json`, `result-message-error.json`, `result-osv-minimal.json`, `result-statement-minimal.json`,
|
||||
`result-error.json`; скрипт `docs/scripts/validate_contracts.py` валидирует все примеры схемами и возвращает 0
|
||||
`result-message-processing.json`, `result-message-done.json`, `result-message-error.json`, `result-osv-minimal.json`,
|
||||
`result-statement-minimal.json`, `result-error.json`; скрипт `docs/scripts/validate_contracts.py` валидирует все
|
||||
примеры схемами и возвращает 0
|
||||
- [ ] 1.5 [docs] `03-architecture.md`: таблица «Контракт обмена через NATS» — ссылки на три файла схем, правила
|
||||
`Nats-Msg-Id`, убрать `group_schema` из конверта; проверить, что текст ссылается на существующие файлы
|
||||
`Nats-Msg-Id`, ack через `processing`, повторная публикация; проверить, что текст ссылается на существующие файлы
|
||||
|
||||
## 2. Инфраструктура разработки
|
||||
|
||||
- [ ] 2.1 [docs] `docs/dev/docker-compose.yml`: сервисы `postgres` (15), `nats` (2.10, `--jetstream`), `minio`,
|
||||
- [ ] 2.1 [docs] `docs/dev/docker-compose.yml`: сервисы `postgres` (16), `nats` (2.10, без `--jetstream`), `minio`,
|
||||
`minio-init` (создаёт бакет `arm-ka`), healthcheck'и, именованные тома; `docs/dev/.env.example`; проверка:
|
||||
`docker compose up -d` → все healthy, `mc ls` видит бакет
|
||||
- [ ] 2.2 [docs] `docs/dev/README.md`: запуск инфраструктуры, переменные, сброс томов, запуск сервисов из venv (черновик,
|
||||
дописывается в 5.2)
|
||||
`docker compose up -d` → все healthy, `mc ls` видит бакет, `curl :8222/varz` отвечает
|
||||
- [ ] 2.2 [docs] `docs/dev/README.md`: запуск инфраструктуры, переменные, сброс томов, запуск сервисов из venv без Nexus
|
||||
(`make venv` фильтрует `raisa_*`), черновик — дописывается в 5.4
|
||||
|
||||
## 3. Ядро (детали — `arm-core/openspec/changes/slice-0-skeleton/tasks.md`)
|
||||
|
||||
- [ ] 3.1 [arm-core] Инструменты: `api/pyproject.toml` (ruff, mypy), `requirements-dev.txt` с `-r requirements.txt`,
|
||||
`Dockerfile` (+ строка убрана из `.gitignore`), `Makefile` (up/test/lint/migrate), `ENV_FILE` в Settings
|
||||
- [ ] 3.2 [arm-core] ORM 1:1 с DBML v5.13 + миграция 0001 + `check_dbml_vs_orm.py`; проверка: upgrade → downgrade → upgrade на compose-PostgreSQL, скрипт сверки — 0 расхождений
|
||||
`Dockerfile` (`python:3.11-slim`, + строка убрана из `.gitignore`), `Makefile` (общие цели + `migrate`, `seed`),
|
||||
`ENV_FILE` в Settings, `raisa-*` в `web/package.json` → `optionalDependencies`
|
||||
- [ ] 3.2 [arm-core] ORM 1:1 с DBML v5.13 + миграция 0001 + `check_dbml_vs_orm.py`; проверка: upgrade → downgrade → upgrade
|
||||
на compose-PostgreSQL 16, скрипт сверки — 0 расхождений
|
||||
- [ ] 3.3 [arm-core] Seed справочников/ролей/прав + dev-пользователи; auth (JWT, dev-эмитент, `require(permission)`), `GET /api/v1/me`
|
||||
- [ ] 3.4 [arm-core] Вендоренный контракт + outbox-паблишер + consumer `recognition.result` + вахтёр `reply_deadline`;
|
||||
интеграционный тест на compose: queued → published → результат → done, дубликат игнорируется
|
||||
- [ ] 3.5 [arm-core] `.gitea/workflows/ci.yml` из шаблона 4.1 (после его готовности), job `web`
|
||||
- [ ] 3.4 [arm-core] Вендоренный контракт + outbox-паблишер + подписка `recognition.result` (queue group `arm-core`): ack
|
||||
`processing`, повторная публикация без ack, дедуп по `result_msg_id`, `dlq`, вахтёр `reply_deadline`; интеграционный
|
||||
тест на compose: queued → published → processing → done; дубликат результата игнорируется; потерянный запрос
|
||||
публикуется повторно
|
||||
- [ ] 3.5 [arm-core] `make check-full` зелёный; итог — в описание PR
|
||||
|
||||
## 4. CI-шаблон (после того как хотя бы один сервис имеет тесты)
|
||||
## 4. Проверки качества (параллельно с 3.x, после первых тестов)
|
||||
|
||||
- [ ] 4.1 [docs] `docs/dev/ci/python-service.yml`: job'ы `lint`, `test` (services: postgres/nats/minio), `contract`,
|
||||
`build`; опциональные шаги arm-core (`migrations`, `dbml-check`); checkout `arm-ka-docs`/`arm-ka-fixtures` по
|
||||
секретам с graceful skip; проверка: workflow проходит в `arm-core` на ветке среза
|
||||
- [ ] 4.2 [docs] `docs/dev/ci/web.yml` (фрагмент job `web` для arm-core: npm ci, biome, tsc, build); проверка — зелёный run в arm-core
|
||||
- [ ] 4.3 [docs] `docs/dev/ci/README.md`: как копировать шаблон, какие секреты нужны (`DOCS_TOKEN`, `FIXTURES_TOKEN`,
|
||||
реестр образов), правило «отличия только в блоке env»
|
||||
- [ ] 4.1 [docs] `docs/dev/checks.md`: общие цели `Makefile` (`up`, `test`, `test-integration`, `lint`, `check`,
|
||||
`check-full`), что делает каждая, порядок перед PR, как пропускаются тесты без фикстур/docs; проверка: цели
|
||||
`arm-core` соответствуют описанию
|
||||
- [ ] 4.2 [docs] Эталонный фрагмент `docs/dev/Makefile.common` (включается в `Makefile` сервисов через `include` или
|
||||
копируется); проверка: `make check` в трёх репозиториях вызывает одинаковые команды (`diff` общих целей)
|
||||
|
||||
## 5. Сервисы распознавания (детали — в их change'ах) и профиль `services`
|
||||
|
||||
- [ ] 5.1 [tabular-parser] Вырезать фронт/БД, вендоренный контракт, worker NATS/S3 с заглушкой `NOT_IMPLEMENTED`, Dockerfile, CI
|
||||
- [ ] 5.2 [scan-gateway] Вырезать фронт/БД, вендоренный контракт, горячие папки (local/smb), эмулятор, worker с
|
||||
таймаутом, Dockerfile, CI
|
||||
- [ ] 5.1 [tabular-parser] Вырезать фронт/БД, вендоренный контракт, подписчик queue group `tabular-parser` с ack
|
||||
`processing` и заглушкой `NOT_IMPLEMENTED`, Dockerfile 3.11, `make check-full`
|
||||
- [ ] 5.2 [scan-gateway] Вырезать фронт/БД, вендоренный контракт, горячие папки (local/smb), эмулятор, подписчик queue group
|
||||
`scan-gateway` с ack и таймаутом, Dockerfile 3.11, `make check-full`
|
||||
- [ ] 5.3 [docs] `docker-compose.yml`: профиль `services` — `arm-core` (8000), `tabular-parser` (8001), `scan-gateway`
|
||||
(8002), `scan-emulator` (том `hotfolder`, `../../fixtures/ocr:ro`); проверка: `--profile services up --build`,
|
||||
все `/healthcheck` 200, stream `RECOGNITION` создан
|
||||
все `/healthcheck` 200 с `nats = ok`
|
||||
- [ ] 5.4 [docs] Сквозная проверка руками по README: вставить `recognition_job` в БД через `psql` (или тестовый скрипт
|
||||
arm-core) → паблишер публикует → `tabular-parser` отвечает `NOT_IMPLEMENTED` → `recognition_job.status = error`,
|
||||
`error_code = NOT_IMPLEMENTED`; то же для `image` через эмулятор (`raw/{job_id}.json` появляется в MinIO);
|
||||
зафиксировать в README как «проверка окружения»
|
||||
arm-core) → паблишер публикует → `tabular-parser` отвечает ack и `NOT_IMPLEMENTED` → `recognition_job.status`
|
||||
проходит `published → processing → error`, `error_code = NOT_IMPLEMENTED`; то же для `image` через эмулятор
|
||||
(`raw/{job_id}.json` появляется в MinIO); остановить `tabular-parser`, вставить задание — через 30 с повторная
|
||||
публикация, после трёх — `dlq`; зафиксировать в README как «проверка окружения»
|
||||
|
||||
## 6. Документы контекста и закрытие
|
||||
|
||||
- [ ] 6.1 [docs] `06-test-data.md` §6: `fixtures/directories/` — только тестовые ЮЛ/ГК (Срез 1); план счетов, правила
|
||||
элиминации, права — `arm-core/api/seed/`; `09-delivery-plan.md` Срез 0: решение D-1 по пакету контрактов,
|
||||
ссылка на `docs/dev/`
|
||||
ссылка на `docs/dev/`, проверки вместо CI
|
||||
- [ ] 6.2 [docs] `07-open-questions.md`: OQ по итогам Среза 0 (контракт `data_type = NU` vs enum `osv_data_type`;
|
||||
обязательность ОСВ расширенного подхода; источник полного плана счетов 1С) и расхождения макета из задачи B;
|
||||
статусы допущений
|
||||
|
||||
@ -3,7 +3,7 @@ schema: spec-driven
|
||||
context: |
|
||||
Репозиторий arm-ka-docs — контекст, модель данных (DBML v5.13), правила домена, контракты между сервисами
|
||||
(docs/contracts/*.schema.json), макет UI и экспорт ТЗ системы АРМ КА (ДКБ, Россельхозбанк).
|
||||
Здесь оформляются СКВОЗНЫЕ изменения: контракт + окружение разработки (docs/dev) + CI-шаблон + изменения
|
||||
Здесь оформляются СКВОЗНЫЕ изменения: контракт + окружение разработки (docs/dev) + проверки качества (make check) + изменения
|
||||
документов контекста. Код сервисов живёт в соседних репозиториях (../arm-core, ../tabular-parser, ../scan-gateway),
|
||||
у каждого свой openspec/ и свой change; сквозной change ссылается на них.
|
||||
Источники истины по приоритету: docs/model/data-model-v5.13.dbml → docs/model/entities-v5.13.md →
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user