feat(#190): метрики качества первички (AURORA-1759) #229
@ -110,9 +110,9 @@ _OURS_COUNTER_KEYS: tuple[str, ...] = (
|
||||
def _value_fix(value: FinancialStatementValue) -> dict[str, Any] | None:
|
||||
"""Правка суммы (§14.2 «Сумма»): `None` — значение не правилось (`is_modified` не `True`).
|
||||
|
||||
Нет сырого текста для сравнения (`source_text is None`) — классифицировать нельзя; по решению design §5
|
||||
«спорное — в наш разбор» (decision log 27.10.2026: анти-цель — не приписывать сервису наши недоработки)
|
||||
считаем правку нашей, не пропускаем (решение задачи 7, см. отчёт)."""
|
||||
Нет сырого текста для сравнения (`source_text is None`) — классифицировать нельзя; считаем правку нашей, не
|
||||
пропускаем (design.md 06.11.2026 — уточнение решения 27.10.2026 «спорное — в наш разбор» на случай «данных
|
||||
нет вовсе», а не «есть, но двусмысленно»; анти-цель §2/§5 — не приписывать сервису наши недоработки)."""
|
||||
if not value.is_modified:
|
||||
return None
|
||||
confirmed = str(value.amount) if value.amount is not None else ""
|
||||
@ -130,7 +130,7 @@ def _value_fix(value: FinancialStatementValue) -> dict[str, Any] | None:
|
||||
|
||||
def _code_fix(line: FinancialStatementLine) -> dict[str, Any] | None:
|
||||
"""Правка кода строки (§14.2 «Код строки»): `None` — код не правился. Нет сырого текста — «наш разбор»
|
||||
(то же решение, что у правки суммы)."""
|
||||
(то же решение design.md 06.11.2026, что у правки суммы)."""
|
||||
if not line.line_code_modified:
|
||||
return None
|
||||
confirmed_code = line.line_code or ""
|
||||
@ -149,13 +149,18 @@ def _code_fix(line: FinancialStatementLine) -> dict[str, Any] | None:
|
||||
|
||||
|
||||
def _added_row_fix(line: FinancialStatementLine, *, is_excel: bool) -> dict[str, Any] | None:
|
||||
"""Добавленная строка (§14.2 «Добавленная строка», `row_index IS NULL` — v5.19). Для PDF — известный пробел:
|
||||
сырые четырёхзначные токены ответа ИС Сканирование Task 2/5 не извлекали, поэтому `raw_line_codes` всегда
|
||||
пуст и результат для PDF всегда «сервис» (design §11 «оставлено исполнителю»; задача 7 это не чинит — вне
|
||||
рамок, см. отчёт). Для Excel — всегда «наш разбор» (`attribute_added_row` сам это решает по `is_excel`)."""
|
||||
"""Добавленная строка (§14.2 «Добавленная строка», `row_index IS NULL` — v5.19).
|
||||
|
||||
Excel — `attribute_added_row` (всегда «наш разбор» при `is_excel=True`, без зависимости от токенов). PDF —
|
||||
сырые четырёхзначные токены ответа ИС Сканирование Task 2/5 не извлекали, `raw_line_codes` для PDF нет совсем
|
||||
(не просто пуст): `attribute_added_row` здесь НЕ вызывается — передача пустой последовательности ошибочно
|
||||
вернула бы «сервис», что противоречит уже принятому 27.10.2026 направлению design.md («совпадение с суммой
|
||||
даёт наш разбор — по правилу „спорное — нам“»). Атрибуция для PDF — «наш разбор» прямо (design.md, 06.11.2026,
|
||||
поправка к задаче 7: деферренный пробел — когда токены появятся, здесь будет настоящий вызов
|
||||
`attribute_added_row` с реальным `raw_line_codes`, а не прямая атрибуция)."""
|
||||
if line.row_index is not None:
|
||||
return None
|
||||
attribution = attribute_added_row(line.line_code or "", (), is_excel=is_excel)
|
||||
attribution: Attribution = attribute_added_row(line.line_code or "", (), is_excel=True) if is_excel else "ours"
|
||||
return {"kind": "added_row", "line_id": str(line.id), "attribution": attribution, "after": line.line_code}
|
||||
|
||||
|
||||
@ -168,14 +173,15 @@ def _header_confirmed_value(meta: FinancialStatementMeta, field: str) -> str:
|
||||
|
||||
def _header_fix(field: str, *, meta: FinancialStatementMeta, header_source: Mapping[str, Any] | None) -> dict[str, Any]:
|
||||
"""Правка поля шапки (§14.2 «Шапка»); вызывается только когда `meta.<field>_modified` уже `True`. Нет сырого
|
||||
текста поля в `header_source` — «наш разбор» (то же решение, что у правки суммы/кода)."""
|
||||
текста поля в `header_source` — «наш разбор» (то же решение, что у правки суммы/кода; design.md 06.11.2026)."""
|
||||
confirmed = _header_confirmed_value(meta, field)
|
||||
source_entry = header_source.get(field) if isinstance(header_source, Mapping) else None
|
||||
source_text = source_entry.get("text") if isinstance(source_entry, Mapping) else None
|
||||
attribution: Attribution = (
|
||||
"ours" if source_text is None else attribute_header_fix(confirmed=confirmed, header_source_text=source_text)
|
||||
)
|
||||
return {"kind": "header", "field": field, "attribution": attribution, "after": confirmed}
|
||||
before = getattr(meta, f"{field}_original_value", None)
|
||||
return {"kind": "header", "field": field, "attribution": attribution, "before": before, "after": confirmed}
|
||||
|
||||
|
||||
def _primary_cause(counters: Mapping[str, int], *, checks_accepted: bool) -> QualityCause:
|
||||
|
||||
@ -229,3 +229,4 @@ PDF, точный вид выгрузки, разбиение на задачи.
|
||||
| 05.11.2026 | В план (`docs/superpowers/plans/2026-11-05-quality-metrics-arm-core.md`) добавлена строка `**Ветка:**` (`feat/190-ocr-metrics`, worktree, issue, Jira) — без неё хук `require-plan.sh` блокировал любую правку в `api/src/**`, так как не находил имя ветки ни в одном файле `docs/superpowers/plans/*.md` | во всех остальных активных планах эта строка есть (сверено на `2026-10-28-corrections-table-ui.md`); её отсутствие — пропуск на этапе написания плана, не содержательное решение | минимальный — чисто метаданные плана |
|
||||
| 05.11.2026 | Коммит задачи 1 включает больше файлов, чем перечислено в брифе (`Makefile`, `README.md`, `api/.env.example`, `api/.env.test`, `api/alembic/env.py`, `api/scripts/check_dbml_vs_orm.py`, `api/scripts/dbml_parity.py`, `api/src/models/base.py`, `api/src/models/__init__.py`, восемь файлов `api/tests/test_*.py`) | все они требуют правки для согласованности версии v5.25 (имя DBML-файла в `Makefile`/README, `HEAD`-ревизия и допущение «всё в dkb» в тестах) или для поддержки второй схемы; список `git add` в брифе соответствовал более узкому ожидаемому дифу до того, как выяснилось, что `check_dbml_vs_orm.py` и тесты миграций не знают о второй схеме | коммит по списку брифа оставил бы `make test`/`make lint`/`make dbml-check` красными — хуже, чем более широкий, но согласованный коммит |
|
||||
| 05.11.2026 | `api/contracts/quality-facts.schema.json` НЕ скопирован из `../docs/contracts/` в Task 1 (`test_contract.py::test_vendored_contracts_are_identical_to_docs` остаётся красным на этом файле) — копирование отложено на задачу, которая реально строит `payload` по этой схеме | файл уже есть в `../docs/contracts/` (сиблинг-репозиторий arm-ka-docs, работа по нему уже слита туда), а в `api/contracts/` копии нет и несовпадение чисто механическое; копия сейчас была бы мёртвой — Task 1 (схема/ORM/миграция) не читает и не пишет `payload`, валидация полезной нагрузки — логика, не схема БД, и не входит в её рамки | следующая задача может не заметить, что это отложено осознанно, и либо повторно принять то же решение не зная о нём, либо оставить контракт невендоренным навсегда |
|
||||
| 06.11.2026 | Task 7 (`CONFIRMED`): нет сырого текста для сравнения у правки суммы/кода/шапки (`source_text`/`line_code_text`/`header_source[field].text` = `None`) — атрибуция «наш разбор» (`ours`), не пропуск правки. Уточняет направление решения 27.10.2026 «Спорное — в наш разбор» на случай «данных для сравнения нет вообще» — это НЕ тот же случай, что пример `1250\n1260` в исходном решении (там текст есть, но двусмысленный); `attribute_*` (Task 3) типизированы на `str`, случай отсутствия текста не предусматривают. Этим же решением подтверждается (не меняется) уже принятое 27.10.2026 направление для добавленной строки PDF (строка «Код добавленной строки ищется среди четырёхзначных токенов…», та же дата) — черновая реализация задачи 7 свела этот случай к вызову `attribute_added_row(code, raw_line_codes=(), is_excel=False)`, который из-за пустого `raw_line_codes` всегда возвращает «сервис», что противоречило уже принятому там направлению «совпадение с суммой даёт наш разбор»; исправлено прямой атрибуцией «наш разбор» для PDF-добавленных строк без вызова `attribute_added_row` (сырых токенов всё равно нет, инфраструктура их извлечения не построена — тот же пробел, что в строке 27.10.2026) | анти-цель §2/§5 («вина сервиса там, где ошиблись мы; спорные случаи — в наш разбор, не в сервис») распространяется и на случай, когда данных для сравнения нет вовсе: отсутствие доказательства ошибки сервиса не есть доказательство ошибки сервиса | если отсутствие сырого текста окажется частым, а не редким краевым случаем, `OUR_ERROR` может быть систематически завышен, а `SERVICE_ERROR` — недосчитан; метрика ослабнет как рычаг давления на поставщика распознавания (прямая цель §1), хотя остаётся безопасным дефолтом по анти-цели §2 |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user