From d57b5bc3781f1b8846a84701d7e4e0c41f2a7919 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 5 Oct 2026 11:42:09 +0400 Subject: [PATCH] =?UTF-8?q?OpenSpec:=20change=20slice-2a-osv-parser=20(?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=B0=D0=BA=D1=82=20v1.1=20rows[?= =?UTF-8?q?].extra,=20=D0=B4=D0=B5=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=B7?= =?UTF-8?q?=D0=B8=D1=86=D0=B8=D1=8F=20=D0=A1=D1=80=D0=B5=D0=B7=D0=B0=202,?= =?UTF-8?q?=20=D1=83=D1=82=D0=BE=D1=87=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=C2=A71=20=E2=80=94=20=D1=81=D0=BF=D0=B5=D0=BA=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: RSHB/arm-ka-docs#9 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2 --- .../slice-2a-osv-parser/.openspec.yaml | 2 + .../changes/slice-2a-osv-parser/design.md | 27 +++++++++++ .../changes/slice-2a-osv-parser/proposal.md | 46 +++++++++++++++++++ .../specs/recognition-contract/spec.md | 21 +++++++++ openspec/changes/slice-2a-osv-parser/tasks.md | 11 +++++ 5 files changed, 107 insertions(+) create mode 100644 openspec/changes/slice-2a-osv-parser/.openspec.yaml create mode 100644 openspec/changes/slice-2a-osv-parser/design.md create mode 100644 openspec/changes/slice-2a-osv-parser/proposal.md create mode 100644 openspec/changes/slice-2a-osv-parser/specs/recognition-contract/spec.md create mode 100644 openspec/changes/slice-2a-osv-parser/tasks.md diff --git a/openspec/changes/slice-2a-osv-parser/.openspec.yaml b/openspec/changes/slice-2a-osv-parser/.openspec.yaml new file mode 100644 index 0000000..e3966d7 --- /dev/null +++ b/openspec/changes/slice-2a-osv-parser/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-05 diff --git a/openspec/changes/slice-2a-osv-parser/design.md b/openspec/changes/slice-2a-osv-parser/design.md new file mode 100644 index 0000000..dc623f6 --- /dev/null +++ b/openspec/changes/slice-2a-osv-parser/design.md @@ -0,0 +1,27 @@ +# Design: Срез 2a (сквозное, docs) + +## Context +См. proposal. Дизайн парсера — в дочернем change `tabular-parser/openspec/changes/slice-2a-osv-parser/design.md` (D-1…D-10). + +## Decisions + +### D-1. MINOR 1.1 вместо «складывать extra в columns» +`columns` — диагностика документа, а не строки; `extra` нужен построчно. Новое необязательное поле — MINOR по правилу +версионирования, схема v1.0 остаётся валидной для старых файлов. *Альтернатива* — ждать 2.0: парсер потеряет данные. + +### D-2. Декомпозиция Среза 2 фиксируется в плане срезов +2a парсер ОСВ → 2b ББ/ОФР из Excel → 2c загрузка и маппинг в ядре (включая решения OQ-01/02/45/67/69/70) → 2d UI. +Каждый подсрез — свой набор change'ей и PR. + +### D-3. Golden-снимки живут в fixtures, формат — файл результата без volatile-полей +Так снимки обновляются вместе с данными и видны всем сервисам; `job_id`, `file_id`, `service.version`, `duration_ms` +исключены, чтобы снимок зависел только от файла и кода парсера. + +## Decisions made without discussion + +| № | Дата | Решение | Обоснование | Цена ошибки | +|---|---|---|---|---| +| 1 | 2026-10-05 | D-1: контракт MINOR 1.1 с `rows[].extra` | §1.2; обратно совместимо | откат поля | +| 2 | 2026-10-05 | D-2: обозначения подсрезов 2a–2d в плане | владелец утвердил объём 2a, остальное — порядок по плану | переименовать | +| 3 | 2026-10-05 | D-3: формат и место golden | см. выше | перенос каталога | +| 4 | 2026-10-05 | Копия схемы v1.1 в arm-core — bounded-PR без логики, после merge docs и tabular-parser | тест идентичности в arm-core | нет | diff --git a/openspec/changes/slice-2a-osv-parser/proposal.md b/openspec/changes/slice-2a-osv-parser/proposal.md new file mode 100644 index 0000000..3f1470e --- /dev/null +++ b/openspec/changes/slice-2a-osv-parser/proposal.md @@ -0,0 +1,46 @@ +# Proposal: Срез 2a (сквозное) — контракт результата v1.1, уточнения §1, фикстуры, OQ + +**Срез:** 2, подсрез 2a «парсер ОСВ» (решение владельца 05.10.2026: сначала только парсер, без ББ/ОФР из Excel, ядра и UI). +**Репозиторий:** arm-ka-docs. **Issue:** RSHB/arm-ka-docs#9. **Дочерний change:** `tabular-parser/openspec/changes/slice-2a-osv-parser` +(RSHB/arm-tabular-parser#5). Фикстуры: RSHB/arm-ka-fixtures#4. + +## Why + +§1.2 требует «неизвестные колонки сохранять в `extra`, не терять», а в контракте v1.0 у строки ОСВ такого поля нет — +парсер не может выполнить правило без MINOR-версии контракта. Кроме того, после реального прогона на 66 файлах §1 и +06-test-data нужно привести к факту, а план срезов — к принятой декомпозиции Среза 2. + +## What Changes + +- **Контракт v1.1** (`docs/contracts/recognition-result.schema.json`): `osvDocument.rows[].extra` — объект + «заголовок колонки → текст ячейки или null» для колонок внутри таблицы, не сопоставленных с суммами и ИНН; `title` + и `description` `schema_version` — «1.1»; пример `examples/result-osv-minimal.json` дополняется `extra`; новый пример + `examples/result-osv-needs-check.json` (нарушение §1.4). Сообщение и запрос не меняются. Спека `recognition-contract`: + требование «Файл результата» — v1.1. +- **`05-domain-rules.md` §1**: уточнения по факту реализации (статусы и коды D-6, формула «стратегия иерархии в + `metrics.warnings` как `hierarchy=<имя>`», TOTAL в `rows`, лимиты парсера, словари по именам файлов), §1.5 — пометка + `ASSUMPTION(OQ-02)`. +- **`06-test-data.md`**: синтетика `osv/synthetic/` (перечень файлов и что проверяет каждый), golden `expected_osv_parse/` + (формат снимка, процесс обновления), xlsb вручную. +- **`09-delivery-plan.md`**: Срез 2 разбит на 2a (парсер ОСВ), 2b (ББ/ОФР из Excel), 2c (загрузка и маппинг в ядре), + 2d (UI «Первичные документы»); `08-source-docs-index.md`/README без изменений. +- **`07-open-questions.md`**: новые OQ (коды валют для строк CURRENCY; поведение при нескольких ОСВ одного счёта в одной + книге), статус OQ-81 (закрыт хелпером), OQ-45/OQ-01/OQ-02 — пометка «допущение парсера, решение в 2c». + +## Capabilities + +### Modified Capabilities +- `recognition-contract`: «Файл результата» — v1.1 с `rows[].extra`; «Версионирование контракта» — без изменений + (MINOR по правилу). + +## Impact + +- Контракт: MINOR, обратно совместимо; копии обновляются в `tabular-parser` (в дочернем change) и `arm-core` (отдельный + bounded-PR: только файл схемы и `OsvRow.extra`, логики нет). Порядок merge: docs → tabular-parser → arm-core. +- Таблицы DBML: нет. Правила: §1 (уточнения без смены смысла), §1.5 (допущение). + +## Non-goals + +- §12 (ББ/ОФР из Excel) и `statementDocument` — не трогаются. +- Изменение семантики `is_cumulative`, `TOTAL`, `NU` в ядре — подсрез 2c (OQ-02, OQ-01, OQ-45). +- `audit_action` для статусов задания (OQ-55), дедуп по attempt (OQ-73). diff --git a/openspec/changes/slice-2a-osv-parser/specs/recognition-contract/spec.md b/openspec/changes/slice-2a-osv-parser/specs/recognition-contract/spec.md new file mode 100644 index 0000000..cc96d96 --- /dev/null +++ b/openspec/changes/slice-2a-osv-parser/specs/recognition-contract/spec.md @@ -0,0 +1,21 @@ +# Spec Delta: recognition-contract + +## MODIFIED Requirements + +### Requirement: Файл результата +Файл результата SHALL соответствовать `docs/contracts/recognition-result.schema.json` v1.1: один файл (`stored_file`) +→ один `job_id` → массив `documents` из `osvDocument` и/или `statementDocument`; денежные значения — строки +`^-?\d+(\.\d{1,2})?$`; `confidence` — 0–100. Пустой массив `documents` допустим только при `status = error`. +У строки `osvDocument.rows[]` SHALL быть необязательное поле `extra` — объект «заголовок колонки → строка или null» для +колонок внутри таблицы, не сопоставленных с шестью суммами и ИНН (§1.2 «не терять»); потребители, не знающие поля, +игнорируют его (MINOR). Производитель v1.1 пишет `schema_version = "1.1"`; файлы v1.0 без `extra` остаются валидными. + +#### Scenario: Результат валидируется примерами +- **WHEN** CI сервиса запускает тесты контракта +- **THEN** все файлы `docs/contracts/examples/result-*.json` проходят схему файла результата, а все + `request-*.json` / `result-message-*.json` — схемы сообщений + +#### Scenario: Лишняя колонка сохранена +- **WHEN** в ОСВ между ИНН и суммами есть колонка «КПП» +- **THEN** у строк контрагентов `extra = {"КПП": "<значение>"}`, файл проходит схему v1.1 и схему v1.0 (поле + игнорируется) diff --git a/openspec/changes/slice-2a-osv-parser/tasks.md b/openspec/changes/slice-2a-osv-parser/tasks.md new file mode 100644 index 0000000..3f57ab9 --- /dev/null +++ b/openspec/changes/slice-2a-osv-parser/tasks.md @@ -0,0 +1,11 @@ +# Tasks: Срез 2a (docs) + +- [ ] 1.1 Контракт v1.1: `contracts/recognition-result.schema.json` (`rows[].extra`, title/description 1.1), примеры + `result-osv-minimal.json` (+`extra`, `schema_version 1.1`) и новый `result-osv-needs-check.json`; `openspec validate --strict`; + проверка схемой через `jsonschema` (скрипт в `dev/checks.md`, если есть, иначе вручную в отчёте). +- [ ] 1.2 `09-delivery-plan.md`: Срез 2 → подсрезы 2a–2d с составом каждого; `07-open-questions.md`: новые OQ (коды валют + CURRENCY-строк; несколько ОСВ одного счёта в книге), OQ-81 закрыт, пометки «допущение парсера» у OQ-01/02/45. +- [ ] 2.1 После реализации парсера (дочерний change, задачи 3.1–4.3): §1 `05-domain-rules.md` — статусы/коды, `hierarchy=<имя>` + в warnings, TOTAL в rows, лимиты, имена словарей; §1.5 — `ASSUMPTION(OQ-02)`; `06-test-data.md` — синтетика, golden, + xlsb; сверка: сценарии спеки `osv-parsing` ↔ пункты §1 (таблица в отчёте). +- [ ] 2.2 После merge дочернего change: `openspec archive slice-2a-osv-parser`; напоминание про bounded-PR копии схемы в arm-core.