From 94846cbd4c6e4bdb86052188f1c9d4dc73aa308c Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Tue, 6 Oct 2026 13:21:30 +0400 Subject: [PATCH] =?UTF-8?q?openspec:=20=D1=81=D0=BA=D0=B2=D0=BE=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D1=81=D0=BF=D0=B5=D0=BA=D0=B0=20=D0=A1?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=D0=B0=202b=20=E2=80=94=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BA=D1=82=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB?= =?UTF-8?q?=D1=8C=D1=82=D0=B0=D1=82=D0=B0=20v1.2,=20=C2=A712,=20OQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposal, design (состав v1.2: DISCLOSURE, statementDocument.checks, границы периода ОСВ; примеры; порядок раскатки потребители → производитель), дельта recognition-contract «Файл результата» v1.2, tasks. Refs #26 Co-Authored-By: Claude Fable 5.1 Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b --- .../slice-2b-statement-parser/design.md | 53 +++++++++++++++++ .../slice-2b-statement-parser/proposal.md | 58 +++++++++++++++++++ .../specs/recognition-contract/spec.md | 39 +++++++++++++ .../slice-2b-statement-parser/tasks.md | 17 ++++++ 4 files changed, 167 insertions(+) create mode 100644 openspec/changes/slice-2b-statement-parser/design.md create mode 100644 openspec/changes/slice-2b-statement-parser/proposal.md create mode 100644 openspec/changes/slice-2b-statement-parser/specs/recognition-contract/spec.md create mode 100644 openspec/changes/slice-2b-statement-parser/tasks.md diff --git a/openspec/changes/slice-2b-statement-parser/design.md b/openspec/changes/slice-2b-statement-parser/design.md new file mode 100644 index 0000000..20cba7c --- /dev/null +++ b/openspec/changes/slice-2b-statement-parser/design.md @@ -0,0 +1,53 @@ +# Design: Срез 2b (сквозное) — контракт v1.2, §12, OQ + +## Context + +Контракт `recognition-result.schema.json` v1.1: `statementDocument` рассчитан на OCR (поля `*_confidence`, +`is_recognized`), `document_type ∈ {BALANCE, INCOME, OTHER}`, результатов проверок у форм нет (у `osvDocument` есть +`checks`); `osvDocument.header` описывает период текстом, годом и кварталом, границы диапазона месяцев выразить нельзя +(OQ-131). Правило версионирования: MINOR — необязательные поля и новые значения enum; потребители обновляют копии раньше +производителя. Потребители копий: `tabular-parser` (тест идентичности в `make check`), `arm-core` (тест идентичности, +pydantic-модели результата в `services/result_mapping.py`), `scan-gateway` (копия и модели). + +## Decisions + +### D-1. Состав v1.2 +- `$defs.statementDocument.properties.document_type.enum`: `["BALANCE", "INCOME", "DISCLOSURE", "OTHER"]`. +- `$defs.statementDocument.properties.checks` (object, необязательное): `relations_failed` и `soft_relations_failed` — + массивы объектов `{column_index: integer, relation: string, expected: money, actual: money}` (все четыре обязательны); + `duplicate_line_codes` — массив строк. Описание: отсутствие объекта — «не проверялось» (OCR-путь), пустые массивы — + «проверено, сошлось». +- `$defs.osvDocument.properties.header.properties`: `period_start`, `period_end` — `["string", "null"]`, `format: date`; + `is_from_year_start` — `["boolean", "null"]`. Описание: границы периода по шапке; для квартала/года — первое и последнее + число; для диапазона месяцев — его границы; `is_from_year_start` = `period_start` — 1 января. +- `title`, `description` схемы и `schema_version.description` — «1.2». `$comment` о версионировании не меняется. +- `recognition-request.schema.json` и `recognition-result-message.schema.json` не меняются; `result_schema_version` + в сообщении — строка, новых значений не требует. + +### D-2. Примеры +`result-statement-minimal.json` получает `checks: {relations_failed: [], soft_relations_failed: [], duplicate_line_codes: []}` +и `schema_version: "1.2"`. Новый `result-statement-excel.json`: `service.name = tabular-parser`, три документа — +`BALANCE` с `locator = "sheet:Бухгалтерский баланс. Лист 1,Бухгалтерский баланс. Лист 2"`, тремя колонками, строками +1110/11101/1100/1600 и `checks` с одним нарушением в `soft_relations_failed`; `INCOME` на одном листе; `DISCLOSURE` без +`columns`/`lines`; `status = needs_check` с предупреждением о единицах. `result-osv-minimal.json` — `period_start`, +`period_end`, `is_from_year_start`. Все примеры валидируются тестами контракта в трёх сервисах. + +### D-3. §12 — текст решений владельца +§12 переписывается по пунктам 1–8 с сохранением нумерации и добавлением п. 9 «Склейка листов и страниц», п. 10 «Статус +документа и книги», п. 11 «Что отложено» (запасной путь по именам, нормализация единиц, признак продолжения). Формулы §7 +остаются в §7; в §12 п. 7 — ссылка и правило «жёсткие/мягкие, допуск ±1, двойной расчёт знаков». Текст решений — из +brainstorming 06.10.2026 (вопросы 2–6) и design дочернего change (D-2…D-8). + +### D-4. Порядок раскатки +docs (этот change) → bounded-PR в arm-core и scan-gateway «копия схемы v1.2 + модели, без логики» → tabular-parser +(производитель 1.2) → fixtures (golden). Тест идентичности в сервисах пропускается только при недоступном docs, поэтому +между мержем docs и мержем копий тесты сервисов на main падают; окно минимизируется одновременным открытием PR. + +## Decisions made without discussion + +| № | Дата | Решение | Обоснование | Цена ошибки | +|---|---|---|---|---| +| 1 | 2026-10-06 | `checks` форм — отдельная структура, не переиспользование `osvDocument.checks` | у форм нарушения по колонкам и соотношениям, у ОСВ — по строкам | поле остаётся необязательным | +| 2 | 2026-10-06 | `expected`/`actual` в нарушении — `money` (строка с двумя знаками), не число | единый тип денег контракта | смена типа — MAJOR, поэтому сразу строка | +| 3 | 2026-10-06 | `is_from_year_start` — отдельное поле, `is_cumulative` не переопределяется | `is_cumulative` ядро уже трактует по OQ-02/124; новое поле не ломает семантику | два похожих поля | +| 4 | 2026-10-06 | Пример `result-statement-excel.json` со статусом `needs_check` и `DISCLOSURE` | иллюстрирует все новые элементы одним файлом | нет | diff --git a/openspec/changes/slice-2b-statement-parser/proposal.md b/openspec/changes/slice-2b-statement-parser/proposal.md new file mode 100644 index 0000000..69f8280 --- /dev/null +++ b/openspec/changes/slice-2b-statement-parser/proposal.md @@ -0,0 +1,58 @@ +# Proposal: Срез 2b (сквозное) — контракт результата v1.2, §12 ББ/ОФР из Excel, OQ + +**Срез:** 2, подсрез 2b «парсер ББ/ОФР из Excel» (решение владельца 06.10.2026: только парсер и контракт; маппинг в ядро — +Срез 3). **Репозиторий:** arm-ka-docs. **Issue:** RSHB/arm-ka-docs#26. **Дочерний change:** +`tabular-parser/openspec/changes/slice-2b-statement-parser` (RSHB/arm-tabular-parser#14). Golden и синтетика — +arm-ka-fixtures (отдельный PR). Копии схемы v1.2 — bounded-PR в arm-core и arm-scan-gateway. + +## Why + +§12 написан до появления реальных файлов. Пакет ДКБ 06.10.2026 принёс 15 форм в трёх раскладках (постраничные xls 1С, +многолистовая книга ФСБУ 4/2023, `TDSheet`) и показал пять решений, которых в §12 нет: страницы одной формы в разных +файлах, единицы «тыс. руб.» против значений с копейками, пятизначные подстроки «в том числе», соотношения §7 как данные, +формы без колонки кодов. Контракту v1.1 не хватает структурного места для результатов проверок §7 и типа «расшифровка» +(DBML `document_type.DISCLOSURE`), а ОСВ со скользящим периодом (OQ-131) — границ периода. Всё это MINOR. + +## What Changes + +- **Контракт v1.2** (`docs/contracts/recognition-result.schema.json`): `statementDocument.document_type` + `DISCLOSURE`; + необязательный `statementDocument.checks` {`relations_failed[]`, `soft_relations_failed[]` — объекты `{column_index, + relation, expected, actual}`; `duplicate_line_codes[]`}; необязательные `osvDocument.header.period_start`, + `period_end` (date|null), `is_from_year_start` (boolean|null). `title`/`description` `schema_version` — «1.2». + Примеры: `result-statement-minimal.json` дополняется `checks`; новый `result-statement-excel.json` (книга с `BALANCE` из + нескольких листов, `INCOME`, `DISCLOSURE`); `result-osv-minimal.json` — новые поля шапки. Сообщение и запрос не меняются. + Спека `recognition-contract`: «Файл результата» — v1.2. +- **`05-domain-rules.md` §12**: решения владельца 06.10.2026 (вопросы 2–6 brainstorming) и уточнения по факту файлов: + классификация листов и продолжений, титул «Бухгалтерская отчетность», склейка листов внутри книги и запрет склейки + между файлами (страница без шапки → `needs_check`), единицы как есть + предупреждение, пятизначные подстроки как строки, + §7 по четырёхзначным с допуском ±1 и двойным расчётом знаков, соотношения — данные `statement_checks.yaml`, отказ от + запасного пути по именам в первой очереди, критерий `ok` документа, порядок документов книги. §7 — пометка, какие + соотношения парсер считает жёсткими, какие мягкими (`ASSUMPTION(OQ-12)`). +- **`06-test-data.md`**: раздел по `statements-xlsx/**` (раскладки, манифест ожиданий, golden `expected_statement_parse/`, + синтетика `st01…st05`). +- **`03-architecture.md`**: версия контракта 1.2, `BookParser` как реализация по умолчанию tabular-parser, порядок + раскатки MINOR (потребители раньше производителя). +- **`09-delivery-plan.md`**: Срез 2b — статус и объём по решению владельца; маппинг форм — Срез 3 (без изменений). +- **`07-open-questions.md`**: новые OQ — единицы «тыс. руб.» против значений с копейками в 1С-выгрузках (кто и где + приводит); форма без колонки кодов (словарь имён из шаблонов); страница-продолжение в отдельном файле (склейка в ядре, + признак `is_continuation` в контракте); строка без кода с суммами в ОФР ФСБУ 4/2023; образец ФСБУ с «АКТИВ» и 1110 в + одной строке. Статус OQ-131: границы периода в контракте v1.2, заполнение — парсер (#11 / 2b). +- **`docs/dev/qa-guide.md` §6.7**: абзац про прогон парсера на формах. + +## Capabilities + +### Modified Capabilities +- `recognition-contract`: «Файл результата» — v1.2 с `DISCLOSURE`, `statementDocument.checks`, границами периода ОСВ; + «Версионирование контракта» — без изменений (MINOR по правилу). + +## Impact + +- Контракт: MINOR, обратно совместимо; копии обновляются в `tabular-parser` (дочерний change), `arm-core` и `scan-gateway` + (bounded-PR: схема, примеры, pydantic-модели; логика не меняется — ядро по-прежнему маппит только `osvDocument`). + Порядок merge: docs → arm-core, scan-gateway → tabular-parser → fixtures. +- Таблицы DBML: нет (`DISCLOSURE` уже есть в `document_type`). Правила: §12 (решения владельца), §7 (пометки). + +## Non-goals + +- Маппинг `statementDocument` в ядре и экран проверки форм — Срез 3. +- Признак продолжения страницы в контракте, нормализация единиц, словарь имён строк — после появления потребителя/файла. diff --git a/openspec/changes/slice-2b-statement-parser/specs/recognition-contract/spec.md b/openspec/changes/slice-2b-statement-parser/specs/recognition-contract/spec.md new file mode 100644 index 0000000..63a5e58 --- /dev/null +++ b/openspec/changes/slice-2b-statement-parser/specs/recognition-contract/spec.md @@ -0,0 +1,39 @@ +# Spec Delta: recognition-contract + +## MODIFIED Requirements + +### Requirement: Файл результата +Файл результата SHALL соответствовать `docs/contracts/recognition-result.schema.json` v1.2: один файл (`stored_file`) +→ один `job_id` → массив `documents` из `osvDocument` и/или `statementDocument`; денежные значения — строки +`^-?\d+(\.\d{1,2})?$`; `confidence` — 0–100. Пустой массив `documents` допустим только при `status = error`. +У строки `osvDocument.rows[]` SHALL быть необязательное поле `extra` — объект «заголовок колонки → строка или null» для +колонок внутри таблицы, не сопоставленных с шестью суммами и ИНН (§1.2 «не терять»). У шапки `osvDocument.header` SHALL +быть необязательные `period_start`, `period_end` (дата или null — границы периода по шапке; для квартала и года — +первое и последнее число, для диапазона месяцев — его границы) и `is_from_year_start` (boolean или null; `is_cumulative` +не переопределяется). `statementDocument.document_type` SHALL допускать `DISCLOSURE` (расшифровка или иная форма +0710xxx: документ без `columns` и `lines`). У `statementDocument` SHALL быть необязательный объект `checks`: +`relations_failed` и `soft_relations_failed` — массивы `{column_index, relation, expected, actual}` (нарушенные +соотношения §7 по колонке; `expected`/`actual` — `money`), `duplicate_line_codes` — массив строк; отсутствие объекта +означает «не проверялось», пустые массивы — «проверено, сошлось». Потребители, не знающие новых полей и значений, +игнорируют их (MINOR). Производитель v1.2 пишет `schema_version = "1.2"`; файлы v1.0 и v1.1 остаются валидными. + +#### Scenario: Результат валидируется примерами +- **WHEN** CI сервиса запускает тесты контракта +- **THEN** все файлы `docs/contracts/examples/result-*.json` проходят схему файла результата, а все + `request-*.json` / `result-message-*.json` — схемы сообщений + +#### Scenario: Лишняя колонка сохранена +- **WHEN** в ОСВ между ИНН и суммами есть колонка «КПП» +- **THEN** у строк контрагентов `extra = {"КПП": "<значение>"}`, файл проходит схему v1.2 и схему v1.0 (поле + игнорируется) + +#### Scenario: Форма из Excel с проверками +- **WHEN** tabular-parser разобрал книгу с балансом на двух листах, ОФР и пояснениями (`examples/result-statement-excel.json`) +- **THEN** файл проходит схему v1.2: `BALANCE` с `locator` из двух имён листов и `checks` с нарушением в + `soft_relations_failed`, `INCOME` с пустыми массивами `checks`, `DISCLOSURE` без `columns`/`lines`; тот же файл + без `checks` и с `document_type = OTHER` вместо `DISCLOSURE` проходит схему v1.1 + +#### Scenario: Границы периода ОСВ +- **WHEN** в шапке ОСВ период «Октябрь 2024 г. – Сентябрь 2025 г.» +- **THEN** `period_start = "2024-10-01"`, `period_end = "2025-09-30"`, `is_from_year_start = false`; файл проходит схему + v1.2, а потребитель v1.1 читает его без ошибок diff --git a/openspec/changes/slice-2b-statement-parser/tasks.md b/openspec/changes/slice-2b-statement-parser/tasks.md new file mode 100644 index 0000000..cbdb4ab --- /dev/null +++ b/openspec/changes/slice-2b-statement-parser/tasks.md @@ -0,0 +1,17 @@ +# Tasks: Срез 2b (сквозное) — контракт v1.2, §12, OQ (arm-ka-docs) + +Задачи выполняются в ветке `feat/26-slice-2b-statement-parser`; детализация — в плане superpowers вместе с дочерним +change tabular-parser. + +- [ ] 1. Контракт v1.2: `docs/contracts/recognition-result.schema.json` — `DISCLOSURE`, `statementDocument.checks`, + `osvDocument.header.period_start/period_end/is_from_year_start`, заголовок и описание «1.2» (design D-1); проверка + схемы валидатором JSON Schema на всех примерах. +- [ ] 2. Примеры: `result-statement-minimal.json` (+`checks`, «1.2»), новый `result-statement-excel.json`, + `result-osv-minimal.json` (границы периода) (design D-2). +- [ ] 3. Спека `openspec/specs/recognition-contract` синхронизируется из дельты при архивации; до этого — дельта в change. +- [ ] 4. `05-domain-rules.md` §12 — решения владельца и уточнения (design D-3); §7 — пометка жёстких/мягких соотношений. +- [ ] 5. `06-test-data.md` — раздел `statements-xlsx/**`, манифест, golden `expected_statement_parse/`, синтетика. +- [ ] 6. `03-architecture.md` — контракт 1.2, `BookParser`, порядок раскатки MINOR; `09-delivery-plan.md` — Срез 2b. +- [ ] 7. `07-open-questions.md` — новые OQ (единицы против копеек; форма без колонки кодов; страница-продолжение в + отдельном файле; строка без кода с суммами в ОФР ФСБУ; образец ФСБУ «АКТИВ»+1110), статус OQ-131. +- [ ] 8. `docs/dev/qa-guide.md` §6.7 — прогон парсера на формах; README `docs/contracts/` (если есть) — v1.2.