Срез 0 (issue #1): раздел «Соглашения шаблона» (что оставлено от шаблона команды, что вырезается, что добавляется); change openspec/changes/slice-0-skeleton — proposal, design с таблицей «Decisions made without discussion», спеки и tasks; в openspec/config.yaml строки с двоеточием взяты в кавычки (YAML разбирал их как словари). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
79 lines
7.9 KiB
Markdown
79 lines
7.9 KiB
Markdown
# 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, CI-шаблон).
|
||
**Соседние:** `../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` в корне
|
||
(строка `Dockerfile` убрана из `.gitignore`), `Makefile` с целями `up / migrate / test / lint / seed`, `.env.example`,
|
||
`.env.test`; `Settings` читает путь к env-файлу из `ENV_FILE` (шаблонный `pytest.ini` уже задаёт его, но `config.py` игнорировал).
|
||
- **Модель данных**: 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): вендоренные схемы контракта `api/contracts/` + pydantic-модели
|
||
`src/contract/`; outbox-паблишер (`queued` → NATS → `published`, `Nats-Msg-Id = <job_id>:<attempt>`), durable
|
||
consumer `recognition.result` (дедуп по `result_msg_id`, обновление статуса/метрик, регистрация файла результата в
|
||
`s3_object`, `dlq` после `max_deliver`), вахтёр `reply_deadline` → `timeout`. Маппинг документов — точка расширения,
|
||
реализуется в Срезе 2.
|
||
- **Аудит**: `services/audit.py` — единственный способ записать `audit_event` (append-only, `before/after` jsonb).
|
||
- **CI**: `.gitea/workflows/ci.yml` из шаблона docs: ruff, mypy, pytest на PostgreSQL/NATS/MinIO, миграции
|
||
upgrade → downgrade → upgrade, `check_dbml_vs_orm`, сборка образа; job `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` уже есть (JetStream API), `miniopy-async` уже есть.
|
||
- **Внешние системы:** NATS JetStream (stream `RECOGNITION`), MinIO/S3 (бакет `arm-ka`), PostgreSQL 15 (`CREATE SCHEMA dkb`,
|
||
`CREATE TYPE`).
|
||
- **Фронт:** без изменений кода; добавляется только CI-job.
|
||
|
||
## 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).
|
||
- Любые экраны фронта.
|