Andrey Epifancev 43cbfe8e38 docs: change slice-0-skeleton под факты платформы — NATS без JetStream, Python 3.11, без CI и Nexus
Обмен с ядром на core NATS: подписка queue group, ack status=processing сразу по получении, process-local
LRU job_id против повторных запросов, публикация результата с повторами; удалены durable consumer / max_deliver /
ack_wait. Dockerfile python:3.11-slim; make check / make check-full вместо CI; raisa_* фильтруются при
dev-установке. Обновлены proposal, design (решения без обсуждения), specs, tasks, CLAUDE.md, openspec/config.yaml.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 16:21:42 +04:00

84 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Proposal: Срез 0 — скелет ядра arm-core
**Срез:** 0 «Скелет» (`../docs/context/09-delivery-plan.md`). **Репозиторий:** arm-core. **Issue:** RSHB/arm-core#1.
**Родительский change:** `../docs/openspec/changes/slice-0-skeleton` (контракт v1.0, compose, проверки качества).
**Соседние:** `../tabular-parser/openspec/changes/slice-0-skeleton`, `../scan-gateway/openspec/changes/slice-0-skeleton`.
## Why
Ядро — единственный писатель в PostgreSQL и единственная точка обмена с сервисами распознавания. Пока нет схемы БД по
DBML v5.13, прав как данных и работающего outbox/inbox, ни один следующий срез (справочники, загрузка файлов, парсер)
не может начаться: каждому нужны таблицы, пользователь с правами и канал в NATS. Срез 0 делает всё это один раз,
без бизнес-логики, и доказывает end-to-end тестом на пустых обработчиках.
## What Changes
- **Инструменты шаблона**: `api/pyproject.toml` (ruff — формат, совместимый с black, и lint; mypy), `requirements-dev.txt`
переписан как `-r requirements.txt` + dev-инструменты (пины шаблона были рассинхронизированы), `Dockerfile`
(`python:3.11-slim` — версия платформы; строка `Dockerfile` убрана из `.gitignore`), `Makefile` с общими целями
`up / test / test-integration / lint / check / check-full` и своими `venv / migrate / seed`, `.env.example`, `.env.test`;
`Settings` читает путь к env-файлу из `ENV_FILE` (шаблонный `pytest.ini` уже задаёт его, но `config.py` игнорировал).
Без Nexus: `make venv` ставит `requirements.txt` без строк `raisa_*`; в `web/package.json` `raisa-ui` и
`raisa-fastapi-protected-web` → `optionalDependencies`.
- **Модель данных**: ORM 1:1 с `../docs/model/data-model-v5.13.dbml` — 43 таблицы, 27 enum'ов, все FK, уникальные и
частичные индексы (`related_group_member` WHERE is_current, `consolidation_lock` WHERE released_at IS NULL,
`document` NULLS NOT DISTINCT), схема `dkb`. Миграция `0001_dbml_v5_13` создаёт всё с нуля и полностью откатывается.
`api/scripts/check_dbml_vs_orm.py` сравнивает DBML и ORM-метаданные; расхождение — ошибка CI.
- **Seed** (`api/seed/`, идемпотентный `python -m scripts.seed`): `regional_office` (ГО), `role` (7 ролей ТЗ 8.1),
`permission` (16 сочетаний объект × действие из §10), `role_permission` по §10 (ASSUMPTION(OQ-16)),
`chart_of_account` (План счетов 94н + субсчета типовой 1С, упомянутые в §4/§6), `elimination_rule` v1 для ББ (§4, 8 правил)
с `elimination_rule_account`, `required_document_rule` для BASIC и EXTENDED (ASSUMPTION(OQ-46)); `--dev`: примеры РФ и
по одному `app_user` на роль.
- **Аутентификация и права**: `core/auth.py` — проверка JWT (`TokenVerifier`: dev HS256-эмитент за флагом
`AUTH_DEV_ISSUER`; каркас `RaisaVerifier` под JWKS/публичный ключ, параметры — вопрос к человеку), поиск `app_user`
по email, зависимости `current_user` и `require(permission_code)`, `GET /api/v1/me`, `POST /api/v1/dev/token`
(только при включённом флаге). Policy-слой (филиал/ЗО/статус) — не здесь (Срез 1+).
- **Обмен через `recognition_job`** (DECISION-4, **NATS без JetStream**): вендоренные схемы контракта `api/contracts/` +
pydantic-модели `src/contract/`; outbox-паблишер (`queued` → NATS → `published`, `Nats-Msg-Id = <job_id>:<attempt>`);
подписка queue group `arm-core` на `recognition.result`: ack сервиса (`status = processing`) → `processing`/`started_at`,
результат → дедуп по `result_msg_id`, обновление статуса/метрик, регистрация файла результата в `s3_object`;
повторная публикация запроса без ack (`ACK_TIMEOUT_SECONDS` = 30, до `OUTBOX_MAX_REPUBLISH` = 3 раз) → затем `dlq`;
вахтёр `reply_deadline` → `timeout`. Маппинг документов — точка расширения, реализуется в Срезе 2.
- **Аудит**: `services/audit.py` — единственный способ записать `audit_event` (append-only, `before/after` jsonb).
- **Проверки качества** (CI в Gitea нет, пайплайн платформы вне репозитория): `make check` — ruff, mypy, pytest без
Docker, идентичность контракта; `make check-full` — плюс интеграционные тесты против compose, миграции
upgrade → downgrade → upgrade, `check_dbml_vs_orm`, сборка образа, `web`: biome, tsc, rsbuild build.
- **Документация репозитория**: `README.md` заменяется описанием сервиса (команды, env, как поднять), `CLAUDE.md` —
раздел «Соглашения шаблона» (уже в ветке).
## Capabilities
### New Capabilities
- `data-model`: схема БД = DBML v5.13; миграции применяются с нуля и откатываются; сверка ORM ↔ DBML.
- `reference-data-seed`: идемпотентная загрузка справочников продукта (роли, права, матрица, план счетов, правила
элиминации, чек-лист) и dev-данных.
- `authn-authz`: вход по JWT, пользователь по email, эффективные права, зависимость `require(permission)`, `/api/v1/me`.
- `recognition-job-exchange`: outbox/inbox через `recognition_job`, идемпотентность, таймауты, DLQ, регистрация результата в `s3_object`.
- `audit-trail`: append-only запись событий `audit_event` через единый сервис.
### Modified Capabilities
<!-- openspec/specs пуст -->
## Impact
- **Таблицы DBML v5.13:** все 43 (ORM и миграция); данными заполняются `regional_office`, `role`, `permission`,
`role_permission`, `chart_of_account`, `elimination_rule`, `elimination_rule_account`, `required_document_rule`,
`app_user` (dev), `user_role` (dev); в рантайме Среза 0 изменяются `recognition_job`, `s3_object`, `audit_event`.
Новых полей и таблиц нет.
- **Параграфы правил:** §4 (seed правил элиминации), §9.4 (жизненный цикл `recognition_status`), §10 (матрица прав).
- **Зависимости (новые):** `PyJWT[crypto]`, `pydbml` (dev, сверка DBML), `ruff`, `mypy`, `pytest-cov`, `types-*`;
`nats-py` уже есть (core NATS, заголовки), `miniopy-async` уже есть.
- **Внешние системы:** NATS без JetStream (subjects `recognition.*`), MinIO/S3 (бакет `arm-ka`), PostgreSQL 16
(`CREATE SCHEMA dkb`, `CREATE TYPE`).
- **Фронт:** код не меняется; `raisa-*` в `package.json` → `optionalDependencies`; проверки `web` входят в `make check-full`.
## Non-goals
- Загрузка файлов, `stored_file`/`document` жизненный цикл и маппинг результата в `osv_entry`/`financial_statement_*` (Срез 2).
- CRUD справочников и экраны администратора (Срез 1); policy-слой по филиалу/ЗО/статусу (Срез 1+).
- Реальная проверка JWT РАИСА (нужны параметры — Open Questions родительского change'а); в Срезе 0 — только dev-эмитент
и конфигурируемый каркас.
- Авто-элиминация ОФР (OQ-09) — правила ОФР в seed не входят.
- Полный план счетов конкретного клиента — только стандартный 94н + типовые субсчета 1С; остальное добавляет администратор (ARCH-004).
- Любые экраны фронта.