arm-core/openspec/changes/slice-0-skeleton/open-questions-proposal.md
Andrey Epifancev db3c1e5141 docs: предложения открытых вопросов в change slice-0-skeleton (I-5, R-26)
openspec/changes/slice-0-skeleton/open-questions-proposal.md — носитель
15 предложенных вопросов, правок статусов OQ-46/OQ-47 и кандидатов в DBML
v5.14 для docs-PR (открывает владелец ../docs); архивируется вместе с
change. Добавлены пункты финального ревью: аудит LOGIN/LOGIN_FAILED,
CORS * и доверенные прокси, открытые /docs и /openapi.json, статус
stored_file из inbox, срок ответа tabular, append-only на уровне БД,
последовательный callback NATS и длинный маппер, проверка ключа
results/{file_id}/{job_id}/ до чтения результата.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 02:01:41 +04:00

20 KiB
Raw Blame History

Назначение: предложения для docs/context/07-open-questions.md и DBML v5.14; docs-PR открывает владелец ../docs. Дата: 2026-10-04 (срез 0, ветка feat/1-slice-0-skeleton; файл архивируется вместе с change slice-0-skeleton, рулинг R-26). Номера OQ-?? присваиваются там, в реестре 07-open-questions.md (подряд с OQ-48), а не здесь.

Предложения для ../docs/context/07-open-questions.md (Срез 0, arm-core) — для PR в docs (R-8)

Формат строк — как в таблице 07-open-questions.md: | ID | Вопрос | Откуда | Влияет на | Статус / допущение |. Занятые номера: OQ-01…OQ-47 (последний — OQ-47). Новые строки ниже помечены OQ-?? (предл. N); при переносе — нумеровать подряд с OQ-48 в порядке предложений. Ничего в ../docs не правилось.

A. Правки существующих строк

ID Вопрос Откуда Влияет на Статус / допущение
OQ-46 (правка статуса) Обязательны ли все ОСВ расширенного подхода (02, 03, 08, 44, 26, 91, 51, 52, 59, 80, 83, 86, 97, 001, 008, 009, 011) при approach = EXTENDED, или часть — «при наличии»? ТЗ 5.1; required_document_rule.is_mandatory срез 0 (seed), срез 3 (чек-лист) допущение принято и применено: все 27 строк EXTENDED в seed (api/seed/core/required_document_rules.yaml, маркер ASSUMPTION(OQ-46)) — is_mandatory = true; смена — правка одного yaml; BASIC — 10 строк, все обязательные
OQ-47 (дополнение) Источник полного плана счетов 1С для seed chart_of_account: в фикстурах файла нет, «план счетов из бандла макета» не передан. Дополнение: названия нестандартных субсчетов (66/67 .06/.22/.24/.26, 76.07.x, 76.08/.28/.38) в seed выведены по их роли в §4 правил, а не взяты из справочника 1С 06-test-data.md §6, fixtures/README.md; api/seed/core/chart_of_accounts.csv срез 0 (seed), срез 1 (справочник счетов) допущение: План счетов 94н (01–99, 001–011) + типовые субсчета 1С БП 3.0, упомянутые в §4/§6 (122 строки: 62 синтетических 94н, 11 забалансовых, 49 субсчетов/подсубсчетов); названия-заглушки заменить по справочнику 1С клиента; остальное добавляет администратор (ARCH-004)

B. Новые вопросы

ID Вопрос Откуда Влияет на Статус / допущение
OQ-?? (предл. 1) Правило элиминации № 1 (§4), сторона Б «76.07.1 − 76.07.5 − 76.07.9»: 76.07.5 и 76.07.9 не потомки 76.07.1, поэтому «вычитание» чисто информативно. Так и задумано? 05-domain-rules.md §4 (правило 1); seed elimination_rules_v1.yaml (T14.1) срез 5 (пары ВГО) допущение: исключения 76.07.5/76.07.9 на стороне Б хранятся как is_excluded и не влияют на сумму (76.07.1 их не содержит); алгоритм на них не опирается; подтвердить с методологами
OQ-?? (предл. 2) Правило № 1, сторона А: резервный счёт 01 лежит в одном списке с 03 и 76.07.9 — приоритета нет. Как выбирать «03, а если у А нет ОСВ 03 — 01»? Что значит «результат в строку ППА» (см. OQ-11)? §4 (правило 1: «01 — резервный»); seed (T14.2) срез 5–6 допущение: в seed accounts: ["03", "76.07.9", "01"] без порядка; fallback «берём 01, если нет ОСВ 03» — в comment правила; логику приоритета реализует срез 5, до решения — не реализуется
OQ-?? (предл. 3) Составные коды статей КФО 1450/1550 (правило 1), 1310/1350 (правило 7, гудвилл) хранятся в elimination_rule.article_2_line одной строкой с «/». Как их потреблять: делить по «/» и подбирать ту строку, что есть в ББ Б, или хранить две строки? §4 (правила 1, 7); seed (T14.3) срез 5–6; DBML (note колонки) допущение: хранится как в §4, потребитель делит по «/»; предложение — зафиксировать это в note article_2_line в DBML v5.14
OQ-?? (предл. 4) required_document_rule: уникальный индекс (approach, document_type, account_id) при nullable account_id допускает дубли строк для не-ОСВ документов (NULL в PostgreSQL различны). Добавить NULLS NOT DISTINCT? DBML v5.13 (строка Indexes { (approach, document_type, account_id) [unique] }); ORM следует DBML буквально (T8.3) DBML v5.14, срез 3 (чек-лист) допущение: ORM/миграция 1:1 с DBML (без NULLS NOT DISTINCT); предложение v5.14: [unique, note: 'NULLS NOT DISTINCT'], как у document
OQ-?? (предл. 5) Контракт v1.0, recognition-result-message: schema_version принимает только 1.x (^1\.\d+$), а result_schema_version — любой MAJOR (^\d+\.\d+$). Намеренно (файл результата версионируется отдельно) или небрежность? docs/contracts/recognition-result-message.schema.json (T20.7) контракт v1.x, срез 2 (inbox/маппинг) допущение: ядро повторяет схему буквально (любой MAJOR проходит валидацию); проверку совместимости MAJOR результата делает маппинг Среза 2; уточнить у владельца контракта
OQ-?? (предл. 6) Контракт v1.0: схемы нигде не задают additionalProperties: false (кроме columns ОСВ). Считать ли неизвестные поля допустимыми расширениями? docs/contracts/*.schema.json (T20) контракт v1.x, все три сервиса допущение: модели ядра extra = "ignore" (строже схемы быть нельзя — иначе ядро отвергнет совместимого пира); изменение — через MINOR контракта
OQ-?? (предл. 7) Параметры JWT РАИСА неизвестны: JWKS URL / публичный ключ, iss, aud, имя claim с email. Что в продуктивном контуре? Сделать проверку aud/iss обязательной в проде? ТЗ 8.1, 03-architecture.md (SSO РАИСА); core/auth.py (T16.2) срез 1+ (прод-конфигурация) допущение: настраиваются AUTH_JWKS_URL/AUTH_PUBLIC_KEY (взаимоисключающие), AUTH_ISSUER, AUTH_AUDIENCE, AUTH_EMAIL_CLAIM (по умолчанию email); при пустых AUTH_AUDIENCE/AUTH_ISSUER aud/iss не проверяются; предложение: в проде сделать aud обязательным и предупреждать при старте, если пусто
OQ-?? (предл. 8) В enum audit_action нет действия для смены статуса задания распознавания (recognition_job). Добавлять ли (DBML v5.14) и нужен ли аудит обмена вообще? DBML v5.13 audit_action; решение R-7 (design #20) DBML v5.14, срез 2 (обмен) допущение: Срез 0 не пишет audit_event из обмена recognition_job; сервис аудита (services/audit.py) готов и покрыт тестами; если нужно — действие в v5.14 и вызовы в recognition_exchange.py
OQ-?? (предл. 9) Append-only аудит обеспечен только на уровне кода (единственная точка записи, статический тест на UPDATE/DELETE по AuditEvent). Нужна ли защита на уровне БД (триггер или REVOKE UPDATE, DELETE для роли приложения)? инвариант «Append-only аудит» (../CLAUDE.md); services/audit.py (задача 21) DBML/миграции, платформа (права БД) допущение: только код + тесты; решение по триггеру/правам — с командой платформы до прода
OQ-?? (предл. 10) Счётчик повторных публикаций запроса (ACK_TIMEOUT_SECONDS × OUTBOX_MAX_REPUBLISH) хранится в recognition_job.metrics.republish_count (jsonb), колонки в DBML нет. Нужна ли колонка (v5.14)? services/recognition_exchange.py (design #6, T23) DBML v5.14, срез replay допущение: в metrics.republish_count; attempt не растёт при повторе; при будущем replay счётчик и reply_deadline надо сбрасывать (T23.1)
OQ-?? (предл. 11) Семантика сроков и ошибок обмена: (а) reply_deadline по умолчанию = now + 5 мин для обоих pipeline (tabular, image), если у задания NULL; (б) при невозможности собрать запрос по контракту задание уходит в error / INVALID_REQUEST; (в) при ACK_TIMEOUT × (MAX_REPUBLISH+1) ≥ 5 мин статус dlq недостижим (раньше сработает timeout). Верно ли? DBML reply_deadline note («OCR — 5 мин»); recognition_exchange.py (T23.4) срез 2–3, настройка таймаутов допущение: как описано (дефолты 30 с × 3 укладываются в 5 мин); сроки по pipeline уточнить с командами парсера и шлюза
OQ-?? (предл. 12) Email пользователя сопоставляется с app_user.email по lower(email); при двух записях, различающихся регистром, — 403 (fail-closed). Добавить CHECK/уникальный индекс по lower(email) в DBML? core/auth.py, api/dependencies/auth.py (R-17, design #29) DBML v5.14, срез 1 (админ-экран) допущение: уникальность email — регистрозависимая (как в DBML); регистронезависимая уникальность — на v5.14
OQ-?? (предл. 13) Seed --dev не защищён флагом DEBUG: создаёт dev-пользователей (в т.ч. ADMIN) на любой БД, на которую указывает .env, и перезаписывает существующего app_user с совпадающим email. Нужна ли защита? scripts/seed.py (T15.2, T15.4) срез 0 (процесс), прод-развёртывание допущение: спека не требует защиты, --dev помечен «только для разработки»; предложение — отказ при DEBUG=false или явный флаг подтверждения
OQ-?? (предл. 14) Неверный метод на существующий API-маршрут (например POST /api/v1/me) отдаёт 404 вместо 405: SPA-монтирование на / перехватывает частичное совпадение, а /api/* в нём принудительно 404. Приемлемо? app.py (SPAStaticFiles, T18.2) API-контракт (клиенты, фронт) допущение: оставлено как есть (честный 404 для несуществующих путей важнее 405); при необходимости — отдельная правка
OQ-?? (предл. 15) Решение по web/package-lock.json и уязвимостям: шаблон lock не содержит, raisa-* из Nexus — lock вне банка отличался бы; в Срезе 0 lock в .gitignore (R-23, design #35). npm install сообщает 6 high (npm audit) в зависимостях шаблона web. Коммитить ли lock; кто обновляет зависимости? web/ (T25.1, task-26-report.md C1) платформа/фронт, до прода допущение: lock не в репозитории, зависимости не менялись; решение — с платформенной командой (Nexus, политика audit)

B2. Новые вопросы из финального ревью ветки (2026-10-04)

ID Вопрос Откуда Влияет на Статус / допущение
OQ-?? (предл. 16) Аудит LOGIN / LOGIN_FAILED (действия есть в audit_action) для JWT-аутентификации: в каком срезе и что считать «входом» — первый запрос с токеном, каждый запрос, отказ проверки токена? DBML audit_action; core/auth.py, api/dependencies/auth.py (финальное ревью) срез 1 (вход, админ-экран), аудит допущение: в Срезе 0 вход не аудируется; сервис аудита готов
OQ-?? (предл. 17) Шаблонные CORS allow_origins=["*"] + allow_credentials=True и ProxyHeadersMiddleware(trusted_hosts=["*"]): в проде любой клиент может подставить X-Forwarded-For, и audit_event.ip_address недостоверен. Какие origins и доверенные прокси на платформе? app.py (шаблон команды); финальное ревью прод-конфигурация, достоверность аудита допущение: оставлено как в шаблоне; перечни origins и доверенных прокси — с платформенной командой до прода
OQ-?? (предл. 18) /docs и /openapi.json FastAPI открыты без аутентификации, в том числе в проде. Закрывать их (выключать при DEBUG=false) или оставить? app.py (шаблон); финальное ревью прод-конфигурация, безопасность допущение: открыты, как в шаблоне; решение — с платформой/ИБ
OQ-?? (предл. 19) Inbox не обновляет stored_file.status (UPLOADED → PROCESSING → PROCESSED / ERROR) и stored_file.current_recognition_job_id по результату задания. Кто и когда это делает в Срезе 2 — inbox или маппер, в той же транзакции? DBML stored_file, file_status; recognition_exchange.py (финальное ревью) срез 2 (загрузка и маппинг) допущение: в Срезе 0 файл остаётся UPLOADED; решение — при проектировании маппера Среза 2
OQ-?? (предл. 11, дополнение) Срок ответа по умолчанию 5 мин для tabular взят по аналогии с OCR: в DBML значение есть только для OCR («OCR — 5 мин»), для парсера — нет. Какой срок у tabular-parser? DBML recognition_job.reply_deadline note; DEFAULT_REPLY_TIMEOUT в recognition_exchange.py (финальное ревью) срез 2, настройка таймаутов допущение: 5 мин для обоих pipeline (см. предл. 11)
OQ-?? (предл. 9, дополнение) Append-only аудита на уровне БД — финальное ревью повторно подняло вопрос; см. предл. 9 (триггер или REVOKE UPDATE, DELETE для роли приложения) инвариант «Append-only аудит»; финальное ревью миграции, права БД допущение: как в предл. 9 — только код + тесты
OQ-?? (предл. 20) Обработчик recognition.result выполняется последовательно в callback подписки NATS; длинный маппер Среза 2 задержит следующие сообщения, ack сервиса не успеет за ACK_TIMEOUT_SECONDS (30 с) — ложный republish, затем dlq. Предложение: развести быстрый inbox (только фиксация сообщения и статуса) и отдельный цикл маппинга результата core/nats.py (subscribe_results), recognition_exchange.py (handle_message, ResultMapper); финальное ревью срез 2 (маппинг), архитектура обмена (D-6) допущение: в Срезе 0 NoopMapper — проблема не проявляется; решение — до реализации маппера Среза 2
OQ-?? (предл. 21) Перед чтением файла результата маппер должен проверять, что ключ начинается с results/{file_id}/{job_id}/, а бакет — бакет роли results (иначе сервис может «подсунуть» чужой объект). Закрепить в контракте и 03-architecture.md? контракт recognition-result-message (result_s3); _register_result_object в recognition_exchange.py; финальное ревью срез 2 (маппинг), контракт v1.x допущение: в Срезе 0 результат только регистрируется в s3_object, не читается; проверку реализует маппер Среза 2

C. Кандидаты в DBML v5.14 (сводка из вопросов выше)

  • required_document_rule: уникальный индекс (approach, document_type, account_id) — NULLS NOT DISTINCT (предл. 4).
  • audit_action: действие для смены статуса recognition_job (предл. 8).
  • recognition_job: колонка счётчика повторных публикаций вместо metrics.republish_count (предл. 10).
  • app_user: уникальность по lower(email) (предл. 12).
  • elimination_rule.article_2_line: note о составных кодах «1450/1550», «1310/1350» (предл. 3).
  • OQ-45 (уже в реестре): NU в osv_data_type — Срез 0 не затрагивает.

D. Заметки для спек/контрактов (не OQ)

  • contracts/recognition-result-message.schema.json: result_schema_version vs schema_version (предл. 5), нет additionalProperties: false (предл. 6).
  • 03-architecture.md / контракт: ключ файла результата — только под results/{file_id}/{job_id}/ в бакете роли results, проверка до чтения (предл. 21).
  • docs/dev/ (compose): тома общие для всех worktree, имя проекта arm-ka.