71 Commits

Author SHA1 Message Date
Andrey Epifancev
7ab16388f2 feat(statement_checks): «родитель = Σ подстрок» не проверяется при пустой детализации (#118, AURORA-1527)
По колонке соотношение считается, только если хотя бы одна подстрока родителя имеет значение
(amount не null; ноль — значение). Прочие соотношения и «пустая сумма = 0» не менялись.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:26:36 +04:00
Andrey Epifancev
11b0216cff feat(statements): parent_code у строк формы ББ/ОФР, общая subline_parents (#118, AURORA-1527)
Правило «подстрока относится к ближайшей четырёхзначной строке выше с теми же тремя цифрами,
иначе к строке из первых четырёх цифр» вынесено в subline_parents; им пользуются проверка
«сумма подстрок = родитель» и ответ GET /documents/{id}/statement (поле lines[].parent_code,
вычисляется, в БД не хранится).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 18:48:50 +04:00
PotapovaA
f9f25e8245 правки по код ревью 2026-10-16 13:19:49 +03:00
Andrey Epifancev
c9d60907d3 Повторная публикация обновляет request_subject; тексты без умолчаний как фактических subject'ов (I-1, M-1, M-3, M-5, #106)
I-1: ветка повторной публикации в _process_one записывает request_subject
(как первая публикация); тест test_republish_updates_request_subject.
M-1: примеры /healthcheck в README (ядро: три subject'а; tabular-parser: два).
M-3: docstring'и и логи не выдают умолчание за фактический subject.
M-5: CLAUDE.md — subject'ы задаются OPENBAO__NATS__NATS_*_SUBJECT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:28:07 +04:00
Andrey Epifancev
9d317b6260 NATS: публикация, подписка и /healthcheck.subjects по настройкам окружения (#106)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:27:22 +04:00
Andrey Epifancev
a6418ca475 fix(consolidation): режим «период из версии» подменяет период только у ОСВ «не того» периода (#103)
Квартальные ОСВ, загруженные в консолидацию своего периода и уже привязанные, в режиме
становились «нарастающими за квартал версии»: select_sources возвращал все такие документы,
и свод брал их строки как есть — консолидация РБ 2024 Q3 на стенде задвоилась бы.
Теперь подмена — только если период шапки версии не подходит (fits_by_header);
регрессионный тест test_mode_keeps_header_period_when_it_fits_version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:42:31 +04:00
Andrey Epifancev
1ae6b8d2da feat(consolidation): временный режим «период ОСВ из версии консолидации» (#103)
Настройка CONSOLIDATION_PERIOD_FROM_VERSION (по умолчанию false; в контуре —
OPENBAO__APP__CONSOLIDATION_PERIOD_FROM_VERSION): ОСВ, загруженная в консолидацию
(stored_file.consolidation_id), считается нарастающей за год и квартал консолидации,
какой бы период ни стоял в шапке. Нужен для тестов на произвольных ОСВ: без него ОСВ
за 1 кв. 2025 в консолидации 2026 Q1 молча не привязывается и свод пуст.

- domain/version_period.py: представление документа с периодом версии (первичка не меняется)
- consolidation_documents.for_consolidation: единая точка подмены; её используют привязка
  (fits, ключ R-1, bind_confirmed, хуки подтверждения и маппинга), свод, чек-лист,
  колонка «ОСВ» периметра (osv_accounts)
- ББ/ОФР и файлы с рабочего стола режим не затрагивает; склейки кварталов в режиме нет
- тесты: unit на чистые функции, интеграционные — сценарий 2025 Q1 → 2026 Q1 с режимом
  и без, файл вне консолидации, чек-лист и карточка, подхват «один слот на счёт»

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:17:01 +04:00
Andrey Epifancev
5899525ca5 docs: README и CLAUDE.md — маршруты файлов консолидации, коды ошибок (#96)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 02:02:24 +04:00
Andrey Epifancev
d5d09d0b5c model: DBML v5.18 — stored_file.consolidation_id, миграция 0006 с backfill (#96)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 01:03:26 +04:00
Andrey Epifancev
a93ab10523 feat(directories): алиасы и ГСК/ГСЗ в POST/PATCH ЮЛ (#84)
Формы «Добавить/Изменить юридическое лицо» (макет 51-02, 79) отправляют всё
одним запросом. POST/PATCH /directories/legal-entities принимают `aliases`,
`gsk_id`, `gsz_id` и применяют их в той же транзакции, что и поля ЮЛ.

- POST: алиасы и членства создаются вместе с ЮЛ; ошибка откатывает всё.
- PATCH: `aliases` — итоговый набор (сверка по normalized_alias; удаление —
  с правом DIRECTORY_DELETE, иначе 403), `gsk_id`/`gsz_id` — итоговые группы
  (`null` — выйти, другая — сменить); не переданное поле не меняется.
- Новая ошибка 422 WRONG_GROUP_KIND; дубль в списке — 409 ALIAS_EXISTS.
- Ответ POST и PATCH — LegalEntityDetail (надмножество LegalEntityOut).
- Логика ручек алиасов и состава ГСК/ГСЗ вынесена в функции модулей
  (add_alias/remove_alias, add_membership/close_membership) — форма и ручки
  ведут себя одинаково; PermissionDeniedError — единый 403 «недостаточно прав».
- Спека directories-api и README обновлены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:48:14 +04:00
Andrey Epifancev
753c10a5a4 docs: актуализировать design/proposal и README после ревью (#80)
M-9: design.md и proposal.md приведены к коду (достройка - _lines_out в
schemas/statements.py, справочник без valid_from/valid_until, ленивая
загрузка с ручными проверками); добавлены R-12 и R-13. T5.1/M-8: сигнатура
PATCH values и обратные кавычки в таблице ошибок README.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 12:33:06 +04:00
Andrey Epifancev
5c330eb400 docs: полный состав строк ББ/ОФР в README и CLAUDE.md (#80)
README: достройка GET, адресация PATCH по line_code, строка UNKNOWN_CELL.
CLAUDE.md: абзац про справочник строк. design.md: рулинги R-9…R-11.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 12:14:53 +04:00
Andrey Epifancev
5ae8ff8a81 nats: subject'ы как в старом АРМ — files.*_uploaded, files.tabular_parsed (#77)
Решение владельца 08.10.2026: recognition.tabular.request → files.tabular_uploaded,
recognition.image.request → files.img_uploaded, recognition.result →
files.tabular_parsed (общий subject ack и результатов; files.img_parsed не
используется). Тела сообщений, Nats-Msg-Id, queue group'ы и schema_version не
меняются. Константы api/src/contract/subjects.py, докстринги, тесты, README,
CLAUDE.md; копии схем api/contracts/ из docs/contracts/ побайтно; change
nats-files-subjects с дельтой спеки. Сквозной change — RSHB/arm-ka-docs#74.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 23:46:18 +04:00
Andrey Epifancev
1b4a0859e4 feat(osv): несопоставленные — только строки без ИНН; ИНН сопоставленного ЮЛ в строке свода (#74)
Решения владельца 08.10.2026:
- отмена П-1: «Осталось N» (`unmapped_total`) и `?unmapped=true` — строки без ЮЛ после резолва и без ИНН из файла;
  строка с ИНН, которого нет в справочнике, сопоставления не требует (`domain.needs_mapping`);
- `ConsolidatedRowOut.counterparty_entity_inn` — ИНН сопоставленного ЮЛ справочника (П-8): фронту — «таблетка»
  с ИНН и пересопоставление у строк, сопоставленных без ИНН в файле.

Фикстуры РБ 2024 Q3: unmapped_total 1379 → 1260. Спека consolidated-osv и README обновлены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:37:18 +04:00
aep
020edd0fdd Merge pull request 'Автоподтверждение чистых документов (#64, OQ-157)' (#69) from feat/64-auto-confirm into main
Reviewed-on: #69
2026-10-08 17:08:34 +03:00
Andrey Epifancev
5f41550a3b docs: README — удаление файла с автоподтверждённым документом (OQ-157)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:52:59 +04:00
Andrey Epifancev
8122205357 fix: правки по финальному ревью автоподтверждения (I-1, M-1, M-2, M-4..M-6, T6.x)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:50:37 +04:00
Andrey Epifancev
e7bef704cf docs: автоподтверждение чистых документов в CLAUDE.md и README (#64)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:31:01 +04:00
Andrey Epifancev
64be2f2b5c feat: имя ГК (company_group_name) в ответах ЮЛ (#67)
В LegalEntityOut (а значит и LegalEntityDetail) добавлено company_group_name: текущее имя ГК company_group_id,
архивной тоже; у монозаёмщика null. Есть во всех ответах /directories/legal-entities: список, карточка, POST,
PATCH, move, withdraw; в снимках истории нет. Имена набора ЮЛ подгружаются одним батч-запросом
(repositories.directories.company_group_names_by_ids), число запросов списка от числа строк не зависит.
Спека directories-api и README дополнены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 17:01:25 +04:00
aep
d8bf5b22dc Merge pull request 'Автозамена: предупреждение о выпавших документах (#58)' (#60) from feat/58-replace-warning into main
Reviewed-on: #60
2026-10-07 23:36:22 +03:00
Andrey Epifancev
081d0e60fb Автозамена: предупреждение о документах заменённого файла, которых нет в новом (#58)
Замена остаётся на уровне файла (§9.5 п. 3, OQ-115), но если у заменённого файла есть
документы, ключа которых (document_type, osv_account_code, год, квартал) нет среди
документов нового файла, в metrics.mapping_warnings задания нового файла добавляется
предупреждение (OQ-154): имя и версия заменённого файла, выпавшие документы (тип, счёт,
период, статус, в т. ч. CONFIRMED) и версии консолидации, с которых сняты привязки
выпавших документов. Поведение замены, FILE_REPLACE и статусы документов не меняются.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 00:33:16 +04:00
Andrey Epifancev
7dcffbd10d Резолв контрагента: алиас приоритетнее ЮЛ-тёзки по нормализованному имени (#57)
CounterpartyIndex.resolve: порядок шагов ИНН -> точное имя -> алиас -> нормализованное
имя (OQ-128, §3 п. 4). Алиас уникален (ALIAS_TAKEN), поэтому владелец алиаса берётся
с match_kind = ALIAS, даже если есть ЮЛ-тёзки по нормализованному имени; NULL остаётся
при нескольких совпадениях только по имени. Правило общее для маппинга (Срез 2c) и
дорезолва свода на чтение (§3 п. 7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 00:21:15 +04:00
Andrey Epifancev
9e53fc64be Схема БД конфигурируемая: настройка DB_SCHEMA вместо зашитой dkb (#38)
DB_SCHEMA (по умолчанию dkb, валидация идентификатора) читают models/base.py (SCHEMA), db/db.py (search_path),
alembic/env.py и миграции 0001-0005 (SCHEMA и строковые FK f"{SCHEMA}.x.id" вместо литералов). В контуре -
OPENBAO__APP__DB_SCHEMA. Офлайн-SQL `alembic upgrade head --sql` при значении по умолчанию совпадает побайтно.
Тесты: валидация настройки, миграции и сессия приложения в схеме dkb_alt; .env.example/.env.test/README/CLAUDE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 19:26:37 +04:00
Andrey Epifancev
a76fc3ed99 Merge main в feat/45-kpp (после #51 и docs#54)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 19:10:20 +04:00
aep
f4a780648d Merge pull request 'feat: «Основной РФ» объектом в ответах ЮЛ и ГК (#47)' (#51) from feat/47-regional-office-object into main
Reviewed-on: #51
2026-10-07 18:01:44 +03:00
Andrey Epifancev
4cc926c033 КПП юридического лица: legal_entity.kpp, миграция 0005, API, импорт (DBML v5.17, #45)
- модели LegalEntity.kpp / LegalEntityHistory.kpp (последней колонкой), миграция 0005_dbml_v5_17 (upgrade/downgrade);
- domain/kpp.py: 9 символов (4 цифры, 2 цифры/латинские A-Z, 3 цифры), у ИП запрещён, иначе 422 INVALID_KPP;
- схемы Create/Update/Out/Detail/HistoryOut, PATCH пишет снимок history и аудит before/after с kpp, поиск q по КПП;
- импорт справочника: необязательная колонка КПП (синоним kpp), код INVALID_KPP, update обновляет непустой КПП;
- dev-seed: необязательный ключ kpp (значений в данных нет, не выдумываем);
- сверка с DBML, Makefile, README, CLAUDE.md, openspec/config.yaml — на v5.17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:51:50 +04:00
Andrey Epifancev
9cceb84827 feat: «Основной РФ» объектом в ответах ЮЛ и ГК (#47)
В LegalEntityOut/Detail/HistoryOut добавлено regional_office, в CompanyGroupOut/HistoryOut — base_regional_office
(RegionalOfficeBrief {id, name, is_head_office} или null); *_id оставлены. Филиалы подгружаются одним батч-запросом
на набор id (списки, карточки, history без FK); неактивный филиал отдаётся. Тесты: совпадение с *_id, null,
число запросов списка не растёт, history — один запрос на филиалы.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:46:59 +04:00
Andrey Epifancev
afc457f593 feat(config): несекретные настройки из OpenBao через OPENBAO__APP__<ИМЯ> (#44)
Контур платформы передаёт в под только OPENBAO__*. Свой источник настроек
(settings_customise_sources): приоритет обычный env -> OPENBAO__APP__* -> .env -> умолчание;
неизвестное имя — ошибка при старте; ключ APP не ломает OpenBAOSettings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:27:06 +04:00
Andrey Epifancev
9cdcb9cb55 docs: README и CLAUDE.md — периметр при создании, ячейки и участник периметра (#42)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 18:02:06 +04:00
Andrey Epifancev
89abf0a8ec openspec: архив change slice-3b-statements-api (2026-10-07) — спеки documents-api, osv-result-mapping, statement-checks и др.
Ссылки README и CLAUDE.md переведены на архив.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 15:23:35 +04:00
Andrey Epifancev
ffc01836b7 fix(statements): единое определение has_lines, inn=null — 422, заголовки отдачи первички, тест подтверждения при нарушении 1370/2400 (M-2, M-5, M-7, M-8)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 14:31:14 +04:00
Andrey Epifancev
dd7f7cc42d fix(files): удаление файла убирает формы ББ/ОФР и комментарии документов (C-1, T8.2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 14:26:34 +04:00
Andrey Epifancev
7578b5fb12 docs(arm-core): правки README и CLAUDE.md по ревью Task 13 (I1–I3, минорные)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 13:44:01 +04:00
Andrey Epifancev
dea64c9f1d docs(arm-core): README, CLAUDE.md, tasks.md под Срез 3b
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 13:30:00 +04:00
Andrey Epifancev
310e63224d feat(files): GET /files/{id}/content — исходный файл потоком из S3, RFC 5987 (D-10)
Поток из S3 порциями без чтения в память; нет объекта — 404 FILE_CONTENT_NOT_FOUND до заголовков;
при обрыве потребителя ответ S3 закрывается и отпускается. Content-Disposition вынесен в common/filenames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 12:55:00 +04:00
Andrey Epifancev
93cd1f8aa0 feat(seed): dev-ГК под документы fixtures — «Русский Бисквит» из выгрузки ЕЦП и владельцы форм ДКБ
seed/dev/company_groups.yaml: ГК «Русский Бисквит» ровно как в fixtures/directories/ecp-export-russkiy-biskvit.xlsx
(20 ЮЛ/ИП, crm_id, филиал «Санкт-Петербургский РФ», ГСК из РБ и Счастливочной), ГК Унагранде/НПЗ/Межениновская по
ИНН из форм ББ/ОФР; алиасы под имена контрагентов и шапок ОСВ («Юг-Финанс / ПЕКАРНЯ СЧАСТЛИВОЧНАЯ», ...), чтобы
документы из fixtures ложились на «своих» ЮЛ и резолвились. Загрузчик проверяет ИНН, виды, ссылки членов и
уникальность «ЮЛ в одной ГСК/ГСЗ» до записи; apply — upsert по name/inn/(ГК, name), коды из последовательностей
API, без удалений. `--no-dev-groups` / `run_seed(dev_groups=False)` — dev-данные без ГК; фикстуры тестов API
используют его, чтобы начинать с пустых справочников.

make check: 1132 passed; интеграция seed/import: 23 passed. Два теста падают и на main из-за обновлённых golden
(test_mapping_golden 88 → 113, test_consolidation_fixtures UNMAPPED_TOTAL) — отдельная issue.

Closes #31

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 17:05:46 +04:00
Andrey Epifancev
5868b14c4d fix(consolidations): верхние границы входов и видимость до FOR UPDATE (M-6)
MembersIn.legal_entity_ids ≤ 500, StatusIn.comment ≤ 1000, OsvFilters.q ≤ 500 (422 со списком ошибок);
lock_visible_version проверяет видимость версии до FOR UPDATE. Спека consolidations-api: требование
«Границы входных данных».

Refs #21

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 13:06:11 +04:00
Andrey Epifancev
98922cc75c docs(slice-4): правки README и design.md по ревью Task 14
Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 08:10:57 +04:00
Andrey Epifancev
2c6908a6e2 docs(slice-4): README и CLAUDE.md по консолидации
README: раздел «Консолидация (Срез 4)» (эндпоинты, статусы, блокировка, привязка, чек-лист, свод, сопоставление, экспорт,
порядок блокировок), коды ошибок Среза 4 в «Ошибки API», раскладка. CLAUDE.md: модули консолидации, правила
(`transition`, порядок блокировок «файл → документ → advisory ГК → версии», FOR NO KEY UPDATE, Q1-эквивалентность).
design.md: решения №82–85; tasks.md: 5.2 выполнена.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 08:02:50 +04:00
Andrey Epifancev
e246531c70 feat(consolidation): кандидаты и ручное сопоставление контрагента с алиасом и автоприменением
GET …/counterparty-candidates (scope group|all, QUERY_TOO_SHORT, recommended по
нормализованному имени строки) и POST …/osv/entries/{entry_id}/counterparty:
проверка строки свода версии, ЮЛ без policy, ALIAS_TAKEN с owners / перенос алиаса,
MANUAL строке и PROPAGATED строкам с тем же strip()-именем без противоречия ИНН,
один OSV_COUNTERPARTY_MAP и LEGAL_ENTITY_CHANGE на каждый алиас. Строки общие для
версий блокируются FOR NO KEY UPDATE ORDER BY id; advisory-блокировка алиаса общая
со справочником. Решения #62–#70 в design.md; 4.3 отмечена.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 06:46:06 +04:00
Andrey Epifancev
eaee3ec5f9 feat(model): DBML v5.16 — counterparty_match_kind, миграция 0004, шаг резолва в маппинге
Enum counterparty_match_kind (INN, NAME, ALIAS, MANUAL, PROPAGATED) и nullable-колонка osv_entry.counterparty_match_kind
(NULL <=> counterparty_id NULL). Миграция 0004_dbml_v5_16: enum, колонка, backfill тремя UPDATE по уже смапленным
строкам (ИНН без пробелов -> INN, trim(имя) -> NAME, остальные -> ALIAS), откат удаляет колонку и тип.
CounterpartyIndex.resolve возвращает Resolution(entity_id, kind) (определение в domain/osv_mapping, R-4);
маппинг пишет kind; EntryOut.match_kind. DBML_VERSION 5.16, Makefile/dbml_parity/CLAUDE/README/config.yaml.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 03:23:02 +04:00
Andrey Epifancev
6ccb99194d feat(model): индексы osv_entry и stored_file в v5.15 (I-1, OQ-117)
- ORM: ix_osv_entry_parent_entry_id, ix_osv_entry_recognition_job_id (models/osv.py),
  ix_stored_file_current_recognition_job_id (models/files.py) — по DBML v5.15 п. 4.
- Миграция 0003_dbml_v5_15: create_index в upgrade, drop_index в downgrade.
- tests/test_migrations.py: индексы есть после upgrade head (btree, не unique), исчезают
  после downgrade на 0002 и возвращаются при повторном upgrade.
- openspec change: design.md решение №53 (I-1 закрыт в v5.15), Migration Plan; дельта
  data-model — 0003 создаёт индексы, откат на шаг их удаляет.
- README «Ограничения Среза 2c»: убран пункт об отсутствии индексов.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 00:56:28 +04:00
Andrey Epifancev
d14295b6d0 docs: ограничения Среза 2c, OQ-117, решения №51–52, уточнения README/CLAUDE.md
- .env.example: UPLOAD_MAX_BYTES — больший файл уходит в rejected[] с кодом
  FILE_TOO_LARGE, ответ 201/422, а не 413 (M-2).
- README «Ограничения»: размер multipart-запроса и число файлов приложением не
  ограничены — лимит ingress платформы (M-4); индексы osv_entry — OQ-117 (I-1);
  S3 — OQ-22 и OQ-111 (T12.3); автозамена частью документов — OQ-115 (T12.2);
  удаление ставит expires_at только объектам папок файла (M-3).
- README/CLAUDE.md: цикл маппинга запускается при NATS_ENABLED и подключённом S3;
  строка NATS_ENABLED в таблице настроек перечисляет цикл маппинга (T12.4).
- tasks.md 3.1: сигнатура counterparty_check(header, owner_inn,
  owner_names_normalized) (T12.5).
- design.md: №51 (R-24, I-1 — решение человека, OQ-117), №52 (R-25, fix wave
  M-1/M-3/T7.2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 00:38:02 +04:00
Andrey Epifancev
f9b8a21410 docs: README/CLAUDE.md Среза 2c, tasks.md
README: разделы «Первичные документы: загрузка», «Статусы файла и документа», «Цикл маппинга», «API файлов и документов»,
«Удаление и замена», «Ограничения Среза 2c»; настройки UPLOAD_MAX_BYTES/MAPPING_POLL_SECONDS/MAPPING_MAX_ATTEMPTS; каталог
кодов ошибок файлов и документов. CLAUDE.md: модули Среза 2c, три фоновых цикла, порядок блокировок, маркер e2e.
OpenSpec: tasks.md (все [x], кроме 5.3; формулировки 1.2/2.1/3.1 по коду), дельта documents-api (job.mapping_error,
DOCUMENT_NOT_CONFIRMABLE), design.md — решения №40–50.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 00:11:52 +04:00
Andrey Epifancev
96525f1ace docs(e2e): предупреждение о конкуренции за recognition.result
e2e подписывается на реальный recognition.result в queue group arm-core: другой экземпляр ядра (контейнер arm-core
профиля services, локальный сервер с NATS_ENABLED=true) заберёт часть ack/результатов. README и docstring теста
требуют остановить такие экземпляры; комментарий: MATCH достигается по имени (ИНН в шапке нет).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 23:58:16 +04:00
Andrey Epifancev
f361fae79b test(e2e): сквозной прогон загрузки ОСВ через compose с реальным tabular-parser
Тест tests/e2e (маркеры integration, e2e, fixtures): полное приложение src.app.app с lifespan на временной БД,
реальные NATS и MinIO, tabular-parser из профиля services; ОСВ 66 за 2024 -> PROCESSED, документ 66/2024/Q4,
9 строк, counterparty_check = MATCH, status = RECOGNIZED; без профиля — skip. Makefile: цель e2e, check-full
включает e2e, test-integration исключает e2e. README: раздел «Сквозной прогон (e2e)».

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 23:55:23 +04:00
Andrey Epifancev
ab606dc59a feat(model): DBML v5.15 — osv_data_type NU, email в нижнем регистре, миграция 0003
- OsvDataType.NU после BU, DBML_VERSION = 5.15, Makefile/dbml_parity/docstring'и на data-model-v5.15.dbml;
- миграция 0003_dbml_v5_15: ADD VALUE 'NU' AFTER 'BU' + lower(app_user.email); downgrade пересоздаёт osv_data_type
  (регистр email не возвращается);
- email хранится в нижнем регистре: users_admin.create_user и seed (_apply_users); с UNIQUE по строчному email гонка
  создания в другом регистре теперь ловится индексом (тест test_api_constraint_races обновлён);
- тесты: enum NU, откат на шаг, lower email на миграции, seed/админка, вставка NU, фиктивная v5.16.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 21:28:04 +04:00
Andrey Epifancev
7b28554817 docs: «нет заголовка Authorization или схема не Bearer» — не попытка входа (ревью R-7, #9)
Формулировка уточнена в докстрингах `services/session_audit.py` и
`api/dependencies/auth.py`, README «Безопасность dev-режима» и CLAUDE.md; тест
событий входа дополнен запросом со схемой Basic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 13:48:52 +04:00
Andrey Epifancev
e8d14edfbb fix: CORS при DEBUG — * без allow_credentials (ревью R-11, #9)
`*` вместе с credentials спецификация CORS запрещает (Starlette тогда отражает
Origin запроса); фронт передаёт токен заголовком Authorization, cookie не
нужны. README «Конфигурация» уточнён.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 13:48:15 +04:00
Andrey Epifancev
2c83005564 fix: seed выдаёт права новых ролей и прав в заполненную матрицу (ревью R-3, R-9, #9)
При непустой `role_permission` вставляются пары, у которых роль или право
вставлены этим же прогоном (`_upsert` возвращает вставленные ключи): их
администратор снять не мог. Прочие пары по-прежнему не трогаются — снятые
права не возвращаются. Убрана мёртвая ветка `unchanged` (R-9): при пустой
таблице совпадений быть не может, при заполненной сверка не нужна.

Тесты: новое право и новая роль во временном seed-каталоге → повторный seed
выдаёт только их пары, снятая связь не возвращается; администратор снял все
связи → матрица заливается заново. README, CLAUDE.md, комментарий
`role_permissions.yaml` и докстринг `seed.py` уточнены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 13:47:42 +04:00