From 6484cd67d0c512d18d3e11e28dc72a85cac52a15 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 7 Nov 2026 09:10:38 +0400 Subject: [PATCH] =?UTF-8?q?AURORA-1734/1735:=20=D1=83=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D1=84=D0=B8?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=BC=D1=83=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E=20=E2=80=94=20=D1=81=D0=B4=D0=B2=D0=B8=D0=BD?= =?UTF-8?q?=D1=83=D1=82=D0=BE=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=20XLSB=5FNO=5FFORMATS=20=D0=B2=20=D1=87?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- context/05-domain-rules.md | 5 +++-- openspec/changes/statement-form-split/design.md | 10 +++++----- .../specs/statement-normalization/spec.md | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index e66a99c..8428337 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -1695,7 +1695,7 @@ file:<имя json>;table:`; документа у такой таблицы одной формы и другой, чем у текущей части (тип части — по большинству кодов её первого сегмента с кодами), начинает новую часть. Часть — обычный кандидат со своими колонками и периодами; первая сохраняет `locator`, `type_hint`, `evidence` и предупреждения адаптера, следующие — `locator` `<исходный>;form:`, без `type_hint` (тип только по - кодам, без `TYPE_CONFLICT`). Сегменты без шапки, с меньшим числом кодов или той же формы (повторная шапка второй + кодам, без `TYPE_CONFLICT`), из предупреждений адаптера — только `XLSB_NO_FORMATS`. Сегменты без шапки, с меньшим числом кодов или той же формы (повторная шапка второй страницы ББ, раздел 25xx после ОФР) не режут; строки другой формы, приклеенные к сегменту без своей шапки, тоже. Разрез — функция пакета `split_by_form`, хост вызывает её перед разбором каждой таблицы или листа; документы частей различаются `file_ordinal` (Р-10), модель и контракт не меняются. @@ -1768,7 +1768,8 @@ file:<имя json>;table:`; документа у такой таблицы «За I полугодие 2025 года» разорвана на «31» и «а I полугодие 2025 года», под «31» — постороннее «$301» в строке 2400): столбец с текстом подписи, не разобранным ни как период, ни как исключение, стоящий **между** колонками периодов (есть левее и правее), с числом не более чем в одной строке с кодом при трёх и более у каждой колонки периода - сегмента, колонкой сумм не считается — его числа идут в то же `NUMBERS_OUTSIDE_TABLE` (`info`) с текстом «числа вне + сегмента и с числами всех колонок периода в строке его числа (число лишнее, а не сдвинутое из пустой ячейки периода), + колонкой сумм не считается — его числа идут в то же `NUMBERS_OUTSIDE_TABLE` (`info`) с текстом «числа вне колонок формы не учитываются: …» (если среди адресов есть такой столбец; иначе прежний текст «правее таблицы»). Столбец с числами в двух и более строках с кодом остаётся колонкой с `COLUMN_WITHOUT_DATE`. 7. **Столбец наименования** — подпись, начинающаяся с «наименование», в строке заголовка; иначе столбец левее кода с diff --git a/openspec/changes/statement-form-split/design.md b/openspec/changes/statement-form-split/design.md index 8fbc806..0ad8a33 100644 --- a/openspec/changes/statement-form-split/design.md +++ b/openspec/changes/statement-form-split/design.md @@ -35,14 +35,14 @@ | 2026-11-06 | Реальные JSON АК-ЮЛ и Калтана в fixtures не кладём; тесты пакета — на синтетических кандидатах (`tests/builders.py`), прогон реальных файлов — вручную через нормализатор scan-gateway | Файлы заказчика с контура; синтетика воспроизводит обе раскладки строка в строку | Низкая | | 2026-11-07 | Тип сегмента для разреза — по первым `min_codes` кодам (все одной формы), а не по большинству всех кодов сегмента (исполнение, Task 2) | Строки ОФР без своей шапки прилипают к сегменту пассива ББ с повторной шапкой; по большинству такой сегмент — «ОФР», и разрез по шапке ББ унёс бы пассив в ОФР | Низкая: сегмент, который открывается 1–2 кодами своей формы и дальше идёт чужой, не режется — как до изменения | | 2026-11-07 | Для разреза — помощник `segments.split_segments` (первая строка, своя шапка, коды сегмента) вместо прямого импорта приватных функций модуля | `split.py` не зависит от приватных имён | Нулевая | +| 2026-11-07 | Редкий столбец отсеивается, только если в строке его числа у всех колонок периода есть числа (финальное ревью, I-1) | Иначе правило молча выбрасывает значение, которое OCR сдвинул из пустой ячейки периода, и документ становится `done` (пассив АК-ЮЛ: итог 1700 = 46922 в столбце «На 2»); сужение совпадает со смыслом решения владельца «постороннее число» | Низкая: обрывок с посторонним числом в строке с пустым периодом остаётся `COLUMN_WITHOUT_DATE` — как до изменения | +| 2026-11-07 | Части `k ≥ 1` получают из предупреждений адаптера `XLSB_NO_FORMATS` (финальное ревью, I-2) | Без него шаг 4 проверяет масштаб xlsb как у файла с форматами: `FRACTIONAL_IN_THOUSANDS` вместо `XLSB_SCALE_SUSPECT` | Нулевая | ## Risks / Trade-offs -- Правило редкого столбца срабатывает и на ББ АК-ЮЛ (прогон 07.11): в сегменте пассива столбец 5 с обрывком «На 2» и - одним числом 46922 (итог 1700 за 31.12.2024, OCR сдвинул его из столбца 6) уходит в `NUMBERS_OUTSIDE_TABLE` (`row:40`). - До изменения этот сегмент сопоставлялся по порядку (`SEGMENT_COLUMNS_MISMATCH`, значения столбца 5 уезжали в колонку - 6, настоящие значения столбца 6 терялись); теперь столбец 6 сопоставлен по дате, потеряно одно число, адрес виден - аналитику, жёсткая проверка `1600 = 1700` ловит пустой 1700. +- Пассив ББ АК-ЮЛ (прогон 07.11): столбец «На 2» с одним числом 46922 — сдвинутый OCR итог 1700 за 31.12.2024 (ячейка + столбца периода в этой строке пуста), поэтому после уточнения I-1 правило не срабатывает: поведение прежнее + (`SEGMENT_COLUMNS_MISMATCH`, `needs_check`). - АК-ЮЛ после разреза: ОФР — отдельный документ, но подписи его колонок 5 («За I п / 20:») и 6 («юлугодие / 25 года») ИС тоже разорвала, и ни одна не разбирается как период — столбец 5 не «между периодами», правило редкого столбца не diff --git a/openspec/changes/statement-form-split/specs/statement-normalization/spec.md b/openspec/changes/statement-form-split/specs/statement-normalization/spec.md index 3f78c51..34034df 100644 --- a/openspec/changes/statement-form-split/specs/statement-normalization/spec.md +++ b/openspec/changes/statement-form-split/specs/statement-normalization/spec.md @@ -13,8 +13,9 @@ Часть — обычный `FormCandidate` для шагов 2–8: строки сетки с первой строки сегмента до начала следующей части, `segment_breaks` — сдвинутые на начало части; `header` — тот же, что у исходного кандидата (шапка файла ИС одна на таблицу); `source` тот же. Первая часть сохраняет `locator`, `type_hint`, `evidence` и `warnings` исходного кандидата; -следующая часть `k` (с 1) получает `locator = ";form:"`, `type_hint = null`, пустые `evidence` и -`warnings` — её тип определяется на шаге 6 только по кодам, без `TYPE_CONFLICT` от заголовка таблицы. Кандидат без +следующая часть `k` (с 1) получает `locator = ";form:"`, `type_hint = null`, пустые `evidence`, +из `warnings` — только предупреждения источника `XLSB_NO_FORMATS` (без них шаг 4 неверно проверяет масштаб xlsb) — её +тип определяется на шаге 6 только по кодам, без `TYPE_CONFLICT` от заголовка таблицы. Кандидат без смены формы SHALL оставаться одним кандидатом без изменений. Пакет SHALL экспортировать функцию разреза (`split_by_form(candidate, rules) → tuple[FormCandidate, ...]`); хост @@ -99,12 +100,13 @@ SHALL считаться числами правее таблицы. Подпи **Редкий столбец с неразобранной подписью** (AURORA-1735, решение владельца 06.11.2026) SHALL НЕ считаться колонкой сумм, если одновременно: у столбца есть текст подписи, не разобранный ни как период, ни как исключение; левее и правее него в сегменте есть колонки значений с подписью периода; число стоит не более чем в одной строке с кодом этого -столбца, а в каждой колонке значений сегмента с подписью периода — не менее чем в трёх. Его числа не попадают в значения +столбца, а в каждой колонке значений сегмента с подписью периода — не менее чем в трёх; в строке этого числа у всех +колонок периода тоже есть числа (число лишнее, а не сдвинутое OCR из пустой ячейки периода). Его числа не попадают в значения строк, `COLUMN_WITHOUT_DATE` не выдаётся; адреса его чисел добавляются в то же предупреждение `NUMBERS_OUTSIDE_TABLE` (`info`). Если среди адресов предупреждения есть адреса такого столбца, текст — «числа вне колонок формы не учитываются: …», иначе прежний «числа правее таблицы формы не учитываются: …». Это разорванная OCR подпись периода («31» + «а I полугодие 2025 года») с посторонним числом под обрывком; столбец с числами в двух и более строках с кодом -остаётся колонкой с `COLUMN_WITHOUT_DATE`. +остаётся колонкой с `COLUMN_WITHOUT_DATE`; единственное число в строке, где ячейка колонки периода пуста, — тоже. Подтверждение SHALL опираться только на уверенные источники, чтобы неуверенное не подтверждало само себя: для неуверенной подписи эффективная дата — дата отчёта с уверенностью 100, иначе самая поздняя абсолютная дата колонок с @@ -157,6 +159,12 @@ SHALL считаться числами правее таблицы. Подпи двух строках с кодом - **THEN** столбец остаётся колонкой с `COLUMN_WITHOUT_DATE` уровня `error` +#### Scenario: Значение, сдвинутое в обрывок подписи +- **WHEN** у столбца «31» между колонками периодов одно число «55» в строке 2460, а ячейка колонки периода 2025 этой строки + пуста +- **THEN** столбец остаётся колонкой с `COLUMN_WITHOUT_DATE` уровня `error`, документ `needs_check` — значение не + теряется молча + #### Scenario: Колонка «Изменение» - **WHEN** правее периодов колонка «Изменение, %» - **THEN** колонка пропускается, предупреждение `COLUMN_SKIPPED` уровня `info`