docs: сценарий «Системное событие» под R-7, решения 98–105 в design.md (I-4)

audit-trail: WHEN сценария «Системное событие» — «системный процесс (без
пользователя) вызывает сервис аудита»; NOTE: обмен через recognition_job
в Срезе 0 аудит не пишет (R-7, design #20). design.md: R-25, R-26 и
решения fix-волны финального ревью (№100–105). openspec validate --strict
проходит.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-04 02:01:41 +04:00
parent db3c1e5141
commit 477f037872
2 changed files with 11 additions and 1 deletions

View File

@ -253,3 +253,11 @@ Seed содержит синтетические счета 01–99 и заба
| 95 | 2026-10-03 | T25: цикл миграций не гоняется отдельным шагом `check-full` (входит в `test-integration`; цель `migration-cycle` — для ручного запуска); `check-infra` — скрипт `api/scripts/check_infra.py` перед `test-integration`, читает адреса из `.env.test`; изменён `test_config.py` (порты) | дубль добавил бы ~10 с; сообщение «поднимите compose командой make up» | цикл миграций не виден отдельной строкой в выводе `check-full` |
| 96 | 2026-10-03 | T26: живой обмен показан скриптом на ORM (не SQL), скрипт лежит рядом с отчётом и не добавлен в репозиторий; `result.json` в MinIO не загружался (NoopMapper только регистрирует `s3_object`); `make check-full` прогнан с `api/.env`, файл удалён | корректные enum и FK; содержимое результата Срез 0 не читает | Срез 2 дополнит демо загрузкой; демо-данные уничтожены `down -v` |
| 97 | 2026-10-04 | T27: OQ-46 и OQ-47 уже есть в реестре — вместо новых номеров предложены правки статусов; новые вопросы — `OQ-?? (предл. N)`, 15 штук; в CLAUDE.md `domain/` помечена «появится в Срезе 3+»; «обязательная проверка CI» заменена на `make dbml-check` (входит в `check-full`); рекомендации (триггер, `NULLS NOT DISTINCT`, обязательный `aud`) — предложения, а не принятые решения | дубли нарушали бы реестр; каталога `api/src/domain/` в Срезе 0 нет; CI в Gitea нет | перенумерация при переносе в docs; правка двух строк и одной строки CLAUDE.md |
| 98 | 2026-10-04 | R-25: сценарий audit-trail «Системное событие» переформулирован под R-7: WHEN — «системный процесс (без пользователя) вызывает сервис аудита» (THEN прежний); под требованием — NOTE, что обмен через recognition_job в Срезе 0 аудит не пишет | прежний WHEN («consumer результатов фиксирует изменение задания») противоречил рулингу R-7 (design #20), спека и код расходились | при появлении аудита обмена в Срезе 2+ сценарий придётся вернуть к конкретному источнику |
| 99 | 2026-10-04 | R-26: `open-questions-proposal.md` хранится в `openspec/changes/slice-0-skeleton/` | DoD требует занести открытые вопросы; `../docs` ведёт другой контроллер; файл архивируется вместе с change и не теряется вместе с ledger | вопросы не попадут в реестр `07-open-questions.md`, если владелец `../docs` не откроет docs-PR |
| 100 | 2026-10-04 | FIX-wave (I-1): тип nullable jsonb — функция `nullable_jsonb()` в `models/base.py` (`JSONB(none_as_null=True)`), сверка ORM↔DBML не менялась (тип определяется по классу), добавлены самотест компаратора и тест-перечень всех nullable jsonb-колонок с флагом | `JSONB.__init__` без аннотаций — mypy strict запрещает вызов в моделях; одна точка вместо шести копий | новая jsonb-колонка без `nullable_jsonb()` — тест-перечень упадёт и подскажет |
| 101 | 2026-10-04 | FIX-wave (I-2): ошибка БД в обработчиках 409/500 логируется одной записью `logger.error` без текста исключения (не `logger.exception`): путь, метод, классы ошибок SQLAlchemy и драйвера, SQLSTATE, SQL с плейсхолдерами (≤ 200 символов), стек без сообщения | проверено: `str()` ошибки asyncpg содержит параметры запроса и DETAIL со значениями ключей (email, ИНН) — персональные данные в логе | при разборе инцидента значения ключей придётся восстанавливать по SQLSTATE и стеку |
| 102 | 2026-10-04 | FIX-wave (I-3): `SMB: SMBSettings = SMBSettings()` (по образцу `ServerSettings` шаблона); `reset_connection_cache` тоже только при заданном `SMB_HOST` и через `asyncio.to_thread`; порядок остановки — задачи обмена → NATS → S3 → SMB → `engine.dispose()`; старт ждёт регистрацию SMB (в потоке), а не уводит её в фон | без SMB сбрасывать нечего; блокирующие вызовы smbclient не должны держать цикл событий; фоновая регистрация усложнила бы остановку | настроенный, но молчащий SMB задержит старт до тайм-аута smbclient (60 с) |
| 103 | 2026-10-04 | FIX-wave (I-5): пункты финального ревью (д) срок ответа tabular и (е) append-only на уровне БД записаны как дополнения к предл. 11 и 9, а не новыми номерами; новые вопросы — предл. 16–21 в разделе B2 | реестр не должен получать дубли | при переносе в docs дополнения надо слить с исходными строками |
| 104 | 2026-10-04 | FIX-wave (I-7): в тесте реального lifespan подменяется `app_module.NatsConnection` на `partial(NatsConnection, result_subject=<уникальный>)` — настоящие `_create_nats` и `_subscribe_results` выполняются; движок БД — заглушка; `OUTBOX_POLL_SECONDS=1`; queue group проверяется по мониторингу NATS (`/connz?subs=detail`, порт 8222) только если он доступен, изменение в БД проверяется всегда | общий `recognition.result` в compose не трогаем; реальный пул БД сессии pytest закрывать нельзя | без мониторинга (другой порт) queue group подтверждается только косвенно — тестом двух инстансов |
| 105 | 2026-10-04 | FIX-wave (M-9, M-11, M-4): команды уровня кластера в тестах (включая `CREATE ROLE` в тесте предвыданной схемы) — через служебную БД `postgres`; в `.env.example` и `.env.test` добавлены `SERVER__HOST=127.0.0.1` и `SERVER__PORT=8000`, новый тест сверяет набор ключей двух файлов (раньше такого теста не было); `OUTBOX_MAX_REPUBLISH=0` допустим (сразу `dlq` после ack-таймаута) | тестам не нужна существующая `armka`; ключи двух env-файлов расходились бы незаметно; 0 повторов — осмысленная настройка | нет |

View File

@ -13,8 +13,10 @@
`description`, `before_value`, `after_value` (jsonb), `comment`, `result`, `changed_field`, `ip_address`, `user_agent`;
`occurred_at` SHALL проставляться сервером (UTC). Запись SHALL выполняться в той же транзакции, что и изменение данных.
NOTE: В Срезе 0 обмен через recognition_job аудит не пишет (рулинг R-7, design #20); сервис готов к использованию Срезом 2+.
#### Scenario: Системное событие
- **WHEN** consumer результатов фиксирует изменение задания и вызывает аудит без пользователя
- **WHEN** системный процесс (без пользователя) вызывает сервис аудита
- **THEN** в `audit_event` появляется строка с `user_id = NULL`, `before_value`/`after_value` содержат старый и новый статус
#### Scenario: Откат транзакции