112 lines
14 KiB
Markdown
112 lines
14 KiB
Markdown
## Context
|
||
|
||
Решение владельца 12.10.2026: «топики можно выставлять через секреты и jwt токен тоже, т.к. у нас будут различные наборы
|
||
топиков и разные jwt для прод и тест окружения». Выбран подход 1 из трёх обсуждённых (12.10.2026):
|
||
|
||
1. **Выбран:** три ключа в группе `OPENBAO__NATS__`, одинаковые в трёх сервисах.
|
||
2. `OPENBAO__APP__*` (`settings_sources.py`) — отвергнут: разносит JWT и subject'ы, на которые он даёт права, по разным
|
||
группам.
|
||
3. Один префикс `NATS_SUBJECT_PREFIX` — отвергнут: работает, только если тестовые subject'ы платформы отличаются от
|
||
боевых префиксом.
|
||
|
||
Образец — `RSHB/armka_file_worker` (проверен в контуре): `NATSSettings` с `NATS_URL`, `NATS_JWT: SecretStr`,
|
||
`NATS_SEED: SecretStr`, `NATS_COMMAND_SUBJECT`, `NATS_EVENT_SUBJECT`; `Settings(hide_input_in_errors=True)`.
|
||
|
||
| Ключ | Умолчание | arm-core | tabular-parser | scan-gateway |
|
||
|---|---|---|---|---|
|
||
| `OPENBAO__NATS__NATS_TABULAR_REQUEST_SUBJECT` | `files.tabular_uploaded` | публикует | подписан (queue `tabular-parser`) | — |
|
||
| `OPENBAO__NATS__NATS_IMAGE_REQUEST_SUBJECT` | `files.img_uploaded` | публикует | — | подписан (queue `scan-gateway`) |
|
||
| `OPENBAO__NATS__NATS_RESULT_SUBJECT` | `files.tabular_parsed` | подписан (queue `arm-core`) | публикует | публикует |
|
||
|
||
## Goals / Non-Goals
|
||
|
||
Goals: subject'ы и учётные данные NATS задаются секретами окружения; разъехавшиеся настройки видны в `/healthcheck`;
|
||
тесты не делят subject'ы со стендом. Non-goals: см. proposal.
|
||
|
||
## Decisions
|
||
|
||
1. **Ключи одинаковые во всех трёх сервисах, проверяются все три.** На окружение — один набор секретов, который
|
||
платформа кладёт всем сервисам; сервис использует только свои subject'ы, но валидирует все три, чтобы ошибка набора
|
||
проявилась в любом сервисе одинаково.
|
||
2. **Валидация при старте.** Имя subject'а — непустое, без пробельных символов, без шаблонов `*` и `>`, без пустых
|
||
токенов (`a..b`, ведущая или завершающая точка). Три имени попарно различны: совпадение запроса и ответа — сервис
|
||
получает собственные ответы; совпадение двух запросов — каждый запрос получают оба сервиса. Ошибка — сервис не
|
||
стартует (ошибка конфигурации, как у `DB_SCHEMA`).
|
||
3. **Константы `contract/subjects.py` — только умолчания.** Код публикации и подписок получает имена из настроек;
|
||
функция выбора subject'а по `pipeline` принимает настройки (форма — на усмотрение плана сервиса).
|
||
4. **Секреты.** `NATS_JWT` и `NATS_SEED` — `SecretStr`; в колбэки nkeys значение передаётся через
|
||
`get_secret_value()`; `Settings` с `hide_input_in_errors=True`, чтобы ошибка валидации конфигурации не печатала
|
||
входные значения.
|
||
5. **`/healthcheck`.** Новый объект `subjects` с subject'ами, которые использует сервис: arm-core —
|
||
`tabular_request`, `image_request`, `result`; tabular-parser — `tabular_request`, `result`; scan-gateway —
|
||
`image_request`, `result`. Отдаётся всегда, в том числе при `nats = off | down` (это конфигурация, не состояние).
|
||
Решение владельца 12.10.2026: «пусть будет в хэлзчеке».
|
||
6. **Лог при старте.** Сервис пишет свои subject'ы одной строкой при старте (существующая строка «Подписка NATS
|
||
активна» сохраняется).
|
||
7. **Тесты (OQ-83).** Интеграционные тесты `tabular-parser` и `scan-gateway`, где обе стороны обмена — в самом тесте,
|
||
задают уникальные subject'ы через настройки и не пересекаются с контейнерами стенда. e2e ядра (`make e2e`) остаётся
|
||
на subject'ах по умолчанию: он гоняет настоящий контейнер `tabular-parser` стенда, который слушает умолчания, а
|
||
конфликт там — в общей queue group `arm-core` на subject'е ответа; требование останавливать ядро стенда сохраняется
|
||
(R-7). Интеграционные тесты NATS ядра идут на уникальном subject'е ответа; публикации запросов в тестах ядра
|
||
(`test_recognition_exchange.py`, `test_api_files_upload.py`) идут на умолчательные subject'ы запросов и достигают
|
||
контейнеров стенда.
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- Разъехавшиеся настройки ядра и сервиса — тишина на шине: ядро переведёт задания в `dlq` по `NO_ACK`. Смягчение:
|
||
`subjects` в `/healthcheck` и лог при старте; в `03-architecture.md` — требование задавать набор всем трём сервисам.
|
||
- JWT окружения без прав на заданные subject'ы — ошибка прав NATS при подписке/публикации (в логе сервиса); смягчение
|
||
— перечень секретов окружения в `03-architecture.md` и `contour-transfer-guide.md`.
|
||
- `/healthcheck` публичен и теперь раскрывает имена subject'ов. Имена не секретны (без JWT ими не воспользоваться),
|
||
JWT и seed в ответ не попадают.
|
||
|
||
## Decisions made without discussion
|
||
|
||
1. 12.10.2026 — **R-1:** имена ключей `NATS_TABULAR_REQUEST_SUBJECT`, `NATS_IMAGE_REQUEST_SUBJECT`,
|
||
`NATS_RESULT_SUBJECT` (по образцу FileWorker — префикс `NATS_`, суффикс `_SUBJECT`), а не `NATS_COMMAND_SUBJECT` /
|
||
`NATS_EVENT_SUBJECT` FileWorker: у нас два subject'а запроса. Имена показаны владельцу в дизайне и приняты вместе
|
||
с подходом. Цена ошибки: низкая — переименование ключа до выката в контур.
|
||
2. 12.10.2026 — **R-2:** `schema_version` не меняется: тела сообщений те же, subject'ы — окружение (как R-2 в
|
||
`nats-files-subjects`). Цена ошибки: низкая.
|
||
3. 12.10.2026 — **R-3:** проверяются все три subject'а в каждом сервисе, включая неиспользуемые им (решение 1).
|
||
Цена ошибки: низкая — лишний отказ старта при кривом наборе, который всё равно сломал бы обмен.
|
||
4. 12.10.2026 — **R-4:** `subjects` в `/healthcheck` отдаётся и при `nats = off`. Цена ошибки: низкая.
|
||
5. 12.10.2026 — **R-5:** пароли MinIO/SMB в `SecretStr` не переводятся — вне запроса владельца; `hide_input_in_errors`
|
||
всё равно скрывает их в ошибках валидации. Цена ошибки: пароли видны в `repr(settings)` до отдельной задачи.
|
||
6. 12.10.2026 — **R-6:** маршрут — OpenSpec change `nats-env-config` в каждом из четырёх репозиториев, один план SDD на
|
||
все четыре (больше пяти задач — SDD-контроллер). Цена ошибки: низкая, процессная.
|
||
7. 12.10.2026 — **R-7:** e2e ядра не переводится на свои subject'ы (решение 7): ему нужен контейнер `tabular-parser`
|
||
стенда на умолчаниях, а развести ядро теста и ядро стенда может только своя queue group, которая в этом change
|
||
не настраивается. OQ-83 для `make e2e` остаётся открытым. Цена ошибки: перед `make e2e` по-прежнему останавливать
|
||
ядро стенда.
|
||
8. 13.10.2026 — **R-8:** задачи 2/4/6 (настройки сервисов) и затем 3/5/7 выполнялись параллельно по репозиториям
|
||
(общих файлов нет); ревью — по каждой задаче отдельно. Цена ошибки: нулевая (разные репозитории).
|
||
9. 13.10.2026 — **R-9:** трейлер коммитов — `Co-Authored-By: Claude Opus 5.5` по вводной; у двух коммитов
|
||
исполнителей (haiku) был Haiku 5.5 — исправлено `commit --amend` до публикации ветки. Цена ошибки: нулевая.
|
||
10. 13.10.2026 — **R-10:** minor-находки задач, усиливающие тесты (T2.1, T2.2, T3.1, T3.2, T4.1, T5.1–T5.3, T5.6, T6.1,
|
||
T6.3, T8.2, T8.3), исправлены одной волной после задач 1–8; косметика (T7.1 — позже исправлена, T7.2, T5.4 —
|
||
позже исправлена) и T8.1 отложены/отклонены в ledger. Тест T2.2 (утечка JWT при невалидном subject) не ловит
|
||
отсутствие `hide_input_in_errors` (ошибка field-validator не печатает вход группы) — оставлен как регрессионный;
|
||
защиту несёт `test_validation_error_hides_nats_secrets`. Цена ошибки: низкая.
|
||
11. 13.10.2026 — **R-11:** по финальному ревью (opus) исправлены I-1 (повторная публикация обновляет
|
||
`recognition_job.request_subject`), M-1 (примеры `/healthcheck`), M-2 (формулировка про интеграционные тесты ядра —
|
||
запросы тестов ядра идут на умолчательные subject'ы), M-3 (docstring'и и логи не называют умолчание фактическим
|
||
subject'ом), M-5 (пометки «по умолчанию» в CLAUDE.md и шаблонах). **M-4** (базовые спеки с `SHALL … files.*`
|
||
формально противоречат ADDED-требованию) — отложено: решение при архивации (MODIFIED) или отдельным change'ем;
|
||
**M-7** (ключи в `.env.example` сервисов закомментированы, у ядра — нет) отклонено: у ядра паритет
|
||
`.env.example`/`.env.test` проверяет тест, в сервисах умолчание работает без ключей. Цена ошибки M-4: после
|
||
архивации в базовых спеках остаются два формально разных требования о subject'ах; M-7: нулевая.
|
||
12. 13.10.2026 — **R-12:** `make check` в docs запускался с `CHANGE=nats-env-config` — дефолт `CHANGE ?= slice-0-skeleton`
|
||
указывает на архивный change (O-2, вне объёма). Цена ошибки: нулевая.
|
||
13. 13.10.2026 — **R-13:** `check-full` в tabular-parser проверен на временной ветке `main` + feat/31: ветка отстаёт от
|
||
main (tp#33), общий `fixtures/` уже содержит golden после fixtures#18; на ветке без merge main 2 теста
|
||
`tests/fixtures` красные, после merge — 2313 passed. Ветку не перебазировал. Цена ошибки: перед merge PR — тот же
|
||
расклад, решение (rebase/merge main) за владельцем.
|
||
14. 13.10.2026 — **R-14:** после отчёта контроллера ветки docs `feat/80` и tabular-parser `feat/31` (ещё не
|
||
опубликованные) перебазированы на свежий `main` (docs#98, tp#33) — снимает расклад R-13 без merge-коммита; force-push
|
||
не нужен. `check-full` сервисов и `make check` docs перезапущены на перебазированных ветках. Цена ошибки: нулевая.
|
||
15. 13.10.2026 — **R-15:** отложенные контроллером RM-1 (лог `file_mismatch` с литералом `files.tabular_parsed`) и RM-2
|
||
(переносы docstring'ов в arm-core) исправлены сразу — правка в три строки, follow-up не нужен. T8.1 сверено с кодом
|
||
ядра: без ack задание после повторных публикаций уходит в `dlq`, файл — `UPLOADED` → `ERROR`; строка `qa-guide.md`
|
||
уточнена. Цена ошибки: нулевая.
|