Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
16 KiB
Context
Сквозной дизайн — change docs unified-statement-normalization (design.md, specs/arm-core/spec.md, плана plans/2026-10-18-plan-3-arm-core.md). Здесь — решения уровня ядра для части А: где лежит код, как идёт миграция, какие допущения сделаны планом. Схема данных — ../docs/model/data-model-v5.19.dbml.
Goals / Non-Goals
Goals и Non-goals — см. proposal.md.
Decisions
- D-1. Часть А не зависит от пакета. До части Б §7 считается на лету по
api/rules/statement_checks.yamlи агрегату формы по коду (P3-14); временный код удаляется в B6. - D-2. Миграция только добавляет. Одна ревизия (0007): новые колонки, таблица, enum'ы, индексы и значения по умолчанию; §7 по существующим формам не пересчитывается (Р-24); откат останавливается на несовместимых данных (спека «Миграция DBML v5.19»).
- D-3.
origin = template⇔row_index IS NULL.line_code_original_value IS NULLдляoriginбольше не используется. - D-4. Порядок A5 — последним (R-1).
Decisions made without discussion
Решения, принятые без обсуждения с человеком на этапе плана и исполнения части А (маркеры P3-n — из плана 3; R-n — рулинги контроллера; Р-n — решения владельца, поверх плана).
| Дата | Решение | Обоснование | Цена ошибки |
|---|---|---|---|
| 2026-10-18 | P3-1. Миграция пишет header_source с ключами inn, report_date, units, organization_name: text — из inn_original_value, report_date_original_value, units_original_value, organization_name; confidence — inn_confidence у inn, у остальных null |
спека: «из *_original_value и inn_confidence»; прочих полей в v5.18 сырыми нет |
подсказка у поля старой формы беднее; формы старых времён на контуре удалены очисткой |
| 2026-10-18 | P3-2. organization_name_modified — false у всех форм (миграция и оба маппера), не NULL |
как inn_modified в маппере 1.x; API отдаёт bool |
одна строка UPDATE |
| 2026-10-18 | P3-3. needs_review_fields: маппер 1.x пишет [], миграция оставляет NULL, чтение NULL = [] |
спека миграции поле не заполняет | нет |
| 2026-10-18 | P3-4. Маппер 1.x тоже хранит вторую форму того же типа (Р-10); формы одного типа и одной пары period_year, period_quarter получают MAPPING_WARNING «в файле есть другая форма <тип> за тот же период» и по D-6 — NEEDS_CHECK (аналог Р-18) |
частичный индекс v5.19 больше не заставляет пропускать; одно правило хранения для обоих MAJOR | O-1: если владелец хочет прежний пропуск — вернуть ветку continue |
| 2026-10-18 | P3-5. Предупреждения маппинга листа ОСВ (повтор счёта, строки без родителя, нарастающая по данным Q1) пишутся ещё и в document_issue документа листа (warning, MAPPING_WARNING, info, core) при любом MAJOR; строки в metrics.mapping_warnings остаются |
спека: DocumentOut.issues ОСВ — «вместе с предупреждениями маппинга ядра этого листа» |
нет |
| 2026-10-18 | P3-6. file_ordinal объединённого документа DISCLOSURE / OTHER — индекс первого такого документа в documents[] |
несозданный документ «номер не занимает» | нет |
| 2026-10-18 | P3-7. Переопределено Р-40 (владелец, 18.10.2026): CODE_NOT_IN_DICTIONARY / CODE_NOT_IN_EDITION пишутся только для четырёхзначных кодов (пятизначные подстроки Excel не дают info; закрывает O-3). Остальное — как ниже. CODE_NOT_IN_DICTIONARY и CODE_NOT_IN_EDITION — одна запись на код документа (line_ids — все строки ok с кодом), пятизначные коды тоже проверяются; при приёме 1.x проверки ядра уровня info не пишутся |
спека для 1.x перечисляет только §7 и MAPPING_WARNING |
O-3 (поток info у пятизначных) |
| 2026-10-18 | P3-8. После правки записи core CODE_NOT_IN_DICTIONARY, CODE_NOT_IN_EDITION, NAME_MISMATCH и STATEMENTS_VERSION_MISMATCH пересчитываются (заменяются); NAME_MISMATCH не ставится строке с code_fixed; MAPPING_WARNING, RESULT_STATUS_INCONSISTENT, LEGACY_WARNING сохраняются |
код строки меняется правкой; страховка статуса — факт приёма | O-4 |
| 2026-10-18 | P3-9. Результат 2.0: значение с column_index вне колонок формы и повтор column_index колонки — первое остаётся, MAPPING_WARNING |
FK значения на колонку; уникальность (meta_id, column_index) |
нет |
| 2026-10-18 | P3-10. Части PATCH применяются в порядке lines → values → columns → header; вся адресация (422) проверяется до первой записи; UNKNOWN_CELL по line_code — по кодам строк ok с учётом назначений lines этого запроса; один line_id дважды в lines — 422 INVALID_LINE_PATCH |
атомарность и предсказуемость | нет |
| 2026-10-18 | P3-11. reference_name — первое имя кода в редакции ответа (edition по дате); нет редакции или кода — null |
спека: «первое имя кода в словаре»; edition ответа — по дате |
нет (Р-29) |
| 2026-10-18 | P3-12. (= Р-35, решение владельца.) Очистка отказывает, только если документ формы упомянут в ex_snapshot_item или привязан к версии в статусе «Верифицирована» или дальше: VERIFIED или ARCHIVED с ex_snapshot.source_version_id = версия; привязки к версиям в работе, на согласовании, REJECTED, CANCELLED (и архивированным отменённым) снимаются, статус версий не меняется |
решение владельца 18.10.2026 (Р-35): верифицированная версия read-only и заморожена в ЕХ, прочие — нет; на стенде версий дальше «в работе» нет | архивированная версия без снимка ЕХ (из CANCELLED) очистке не мешает — если бы мешала, владелец увидел бы лишний отказ |
| 2026-10-18 | P3-13. Коды выхода purge_statements.py: 0 — отчёт или выполнено, 1 — отказ (п. 2 спеки), 2 — ошибка аргументов |
как у check_dbml_vs_orm.py |
нет |
| 2026-10-18 | P3-14. До части Б §7 считает ядро на лету (statement_checks.yaml) по агрегату; checks.failed[].codes берутся из правила, line_ids — по агрегату |
часть А должна быть самосогласованной | временный код удаляется в B6 |
| 2026-10-18 | P3-15. Для условия RECOGNIZED 2.0 предупреждения файла берутся из metrics.warnings файла результата; в recognition_job.metrics.warnings — из конверта (inbox) |
маппер читает файл результата; по контракту наборы одинаковы | нет |
| 2026-10-18 | P3-16. (= Р-34, решение владельца.) Подтверждение кода (lines[] {confirmed: true}) ставит is_confirmed = true и needs_review = false, line_code_confidence и line_code_modified не меняет; назначение кода — confidence = 100, line_code_modified = true |
иначе исходная уверенность теряется, а снятие подтверждения не может вернуть needs_review (гайд §5.6.1: «при false — пересчитывается»); аналогия с ячейкой (is_modified не ставится); подтверждено владельцем 18.10.2026 (Р-34), спека arm-core, §9.5 п. 9 и гайд §5.6.1 приведены к этому |
нет |
| 2026-10-18 | P3-17. Порядок строк ответа: строки ok (с шаблонными) — по коду, затем row_index (NULL последними), затем id; за каждой строкой ok источника — её «вложения» по row_index: строки detail с этим родителем и строки unreadable / missing / detail без родителя, у которых она — ближайшая предыдущая по row_index строка ok источника; вложения без такой строки — в начале |
спека «Ответ GET …/statement» |
нет |
| 2026-10-18 | P3-18. Уточнено Р-41 (владелец): смена crm_id пишется в аудит как изменение группы (GROUP_CHANGE) — закрывает O-6. Изменение crm_id ГСК/ГСЗ пишет одно событие GROUP_CHANGE с before/after всех изменённых полей вместо MEMBERSHIP_CHANGE |
спека: «изменение — аудит GROUP_CHANGE» |
O-6 |
| 2026-10-18 | P3-19. Ядро зовёт у пакета choose_edition (engine.edition) и status_input_from_contract (engine.contract_document_status) вместо сборки StoredForm ради редакции и своего status_input_from_v2; to_contract ядру не нужен |
одно правило с сервисом и с recompute (план 1, P1-25, P1-27); пары (код, уверенный) — из строк ok результата по row_index |
при ошибке сборки пар редакция приёма разойдётся с редакцией после правки — тест B1 сравнивает их |
| 2026-10-18 | P3-20. header.source.<поле>.confidence — строка Decimal с двумя знаками ("61.54"), как прочие уверенности API; в JSONB header_source остаётся число как пришло |
фронт читает все уверенности одним способом (план 6, A8) | округление до сотых в подсказке; исходное число — в БД |
| 2026-10-18 | P3-21. StatementHeaderOut.okei — из financial_statement_meta.okei (колонка есть с v5.18), только чтение; в PATCH header не входит |
спека header контракта несёт okei, экран показывает issues с field = "okei" у поля |
нет |
| 2026-10-18 | P3-22. Справочник строк для подсказки кода — поле StatementOut.edition_lines [{code, name}], а не отдельный эндпоинт: данные уже загружены (LineCatalog, A6) при сборке ответа, нет нового маршрута, права, кэша и запроса на фронте; около 80 пар на форму (≈ 4 КБ) против строк формы с значениями; у формы без даты — коды всех редакций, по одному |
наименьший объём реализации в ядре и во фронте; закрывает пробел «у формы без даты подсказки нет» (план 6, O-3) | ответ GET тяжелее на ≈ 4 КБ; если понадобится справочник вне формы — отдельный эндпоинт поверх того же LineCatalog.edition_lines |
| 2026-10-18 | P3-23. DocumentBrief.issue_counts {error, info} — все записи document_issue документа (и relation / soft, в том числе акцептованные) по severity; один групповой запрос на файл |
панели файлов нужен признак «есть что смотреть» без запроса на каждый документ (план 6, O-4) | акцептованное нарушение продолжает помечать документ в панели — подробности на экране документа |
| 2026-10-18 | P3-24. Ошибки элемента lines[] (409 LINE_CODE_TAKEN, 422 LINE_CODE_REQUIRED, INVALID_LINE_CODE, INVALID_LINE_PATCH, UNKNOWN_LINE) несут в detail line_id и line_code первого неверного элемента; LINE_CODE_REQUIRED добавлен к четырём кодам запроса фронта |
экран ставит ошибку у поля нужной строки, не угадывая (план 6, O-2); одно правило для всех ошибок элемента | при нескольких неверных элементах фронт видит только первый — как и прежде |
| 2026-10-18 | R-1. Порядок исполнения части А: A0–A4, затем A6–A13; A5 (контракт 2.0, recognition_exchange.py) — последней, после слияния плана 0 (в origin/main нет api/tests/contract_identity.py) |
A5 зависит от теста идентичности плана 0, остальные задачи его результат не потребляют | возможные конфликты слияния в documents.py / recognition_exchange.py при A5 в конце |
| 2026-10-18 | R-2. Исполнители задач и их ревьюеры — sonnet; финальное ревью ветки — opus | план даёт прозу, а не готовый код; ревью ветки ловит сквозные расхождения | более слабая модель на задаче — лишние итерации ревью |
| 2026-10-18 | R-3. Тесты с БД идут на scratch-БД armcore_pytest_* compose-Postgres :5432 (устройство conftest); контейнеры не останавливаются и не пересоздаются |
общий стенд разработки не должен падать из-за прогонов | недоступный Postgres — задача BLOCKED, а не пропуск тестов |
Открытые вопросы плана (O-n) — в .superpowers/sdd/2026-10-18-plan-3-arm-core/p3-decisions.md (ledger, в git не попадает); закрытые владельцем: O-2 (Р-34), O-3 (Р-40), O-5 (Р-35), O-6 (Р-41). Решения исполнителей по ходу задач A1–A13 дописываются сюда контроллером.