191 Commits

Author SHA1 Message Date
Andrey Epifancev
cea8dfe485 feat(normalizer): очистка OCR-текста — двойники, разделитель клеток, clean_value (§11.1 п. 1, 6)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:56:16 +04:00
Andrey Epifancev
ecdf9a4e13 fix(normalizer): опечатка кода 1220 в справочнике 66n, тест данных редакции
I-1: Исправлена опечатка в api/rules/statement_lines.yaml код 1220:
- было: 'ценностями' (неправильный падеж)
- стало: 'ценностям' (правильный падеж, соответствует брифу)

I-2: Добавлен тест test_66n_edition_data_integrity() в TestRealRules:
- проверяет точное количество кодов в редакции 66n (63)
- валидирует специфические коды из брифа: 1220 (опечатка), 1105, 2411, 2412
- тест фиксирует данные редакции 66n, предотвращает проскальзывание опечаток

Результат: 760 тестов пройдено (749 + 11 новых), lint чист.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:44:44 +04:00
Andrey Epifancev
393d65208f feat(normalizer): справочники yaml с редакциями форм, загрузчик правил, SCAN_CLEANUP/SCAN_RULES_DIR (D-7)
Справочники нормализатора OCR:
- api/rules/statement_lines.yaml: редакции форм ББ/ОФР (66n до 2024-12-31, fsbu4 с 2025-01-01)
- api/rules/ocr_lookalikes.yaml: замены букв на цифры (из date_utils._LOOKALIKES)
- api/rules/statement_labels.yaml: подстроки label → тип документа (BALANCE/INCOME)

Модуль rules.py:
- Edition: редакция с интервалом дат и таблицей кодов → наименований
- OcrRules: все справочники + методы edition_for(), edition_by_codes(), known_codes(), editions_with()
- load_rules(): загрузчик с валидацией (коды 4–5 цифр, двойники 1→1, файлы обязательны)

Настройки:
- SCAN_CLEANUP: удаление файлов из горячей папки (по умолчанию false)
- SCAN_RULES_DIR: путь к справочникам (по умолчанию 'rules')

TDD: 10 тестов (TestRealRules, TestEditionChoice, TestValidation, test_settings_defaults)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:33:54 +04:00
Andrey Epifancev
533e5a2a4a Merge remote-tracking branch 'origin/main' into feat/6-slice-3a-ocr-normalizer 2026-10-06 23:27:43 +04:00
Andrey Epifancev
86fa779c0c openspec/plan: Срез 3a приведён к DBML и 2b — повтор кода (№15), string_value только для нечисла (№16)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:27:42 +04:00
aep
7653860bb2 Merge pull request 'chore(contract): копия docs/contracts после M-12 ($comment, примеры)' (#7) from fix/contract-v1-2-examples into main
Reviewed-on: #7
2026-10-06 22:21:40 +03:00
Andrey Epifancev
55c017e819 chore(contract): копия docs/contracts — $comment без «1.1», пример Excel done, OCR-пример без checks, описание мягких нарушений (M-12, OQ-138)
Refs RSHB/arm-tabular-parser#20

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-06 23:17:33 +04:00
Andrey Epifancev
b1da9b8774 plan: Срез 3a — Task 0 выполнена (main с v1.2 влит, 749 passed), OQ с 144, CONTRACT_VERSION 1.0 (решение №14)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 22:48:40 +04:00
Andrey Epifancev
3bc01e5e23 Merge remote-tracking branch 'origin/main' into feat/6-slice-3a-ocr-normalizer 2026-10-06 22:46:47 +04:00
aep
d13c65d172 Merge pull request 'chore(contract): копия схемы v1.2 (без логики)' (#5) from chore/contract-v1-2 into main
Reviewed-on: #5
2026-10-06 21:43:48 +03:00
Andrey Epifancev
ec916aee3f plan: Срез 3a — план реализации (18 задач), решение №13 в design.md
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 19:19:24 +04:00
Andrey Epifancev
0894d2b89b openspec: решение №12 (скобка как }) — в открытые вопросы, follow-up в docs OQ (решение владельца)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 18:59:07 +04:00
Andrey Epifancev
a15fe65d52 openspec: спека Среза 3a — горячие папки как у armka_file_worker, нормализатор OCR → statementDocument v1.2 (#6)
proposal, design (D-1…D-9, решения без обсуждения №1–12), дельты спек image-recognition-gateway
(папки, очистка под флагом, нормализация, справочники с редакциями, статусы) и scan-emulator (пара по sha256,
набор по умолчанию). tasks.md заполняется планом.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 18:39:52 +04:00
Andrey Epifancev
e2f1bb5f4a chore(contract): копия схемы v1.2 и модели (DISCLOSURE, checks, границы периода), без изменения логики
Refs RSHB/arm-tabular-parser#14

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 14:30:44 +04:00
aep
dffc18a2f1 Merge pull request 'chore(openspec): архив change slice-0-skeleton' (#4) from chore/archive-slice-0-skeleton into main
Reviewed-on: #4
2026-10-04 18:48:43 +03:00
Andrey Epifancev
7cec4596d9 chore(openspec): архив change slice-0-skeleton
Срез 0 реализован и влит (PR #3), change перенесён в openspec/changes/archive, спеки — в openspec/specs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-04 19:44:47 +04:00
aep
8bb10949ec Merge pull request 'Срез 0: скелет scan-gateway — без фронта и БД, горячие папки local/smb, воркер NATS без JetStream, эмулятор ИС Сканирование' (#3) from feat/1-slice-0-skeleton into main
Reviewed-on: #3
2026-10-04 18:41:18 +03:00
Andrey Epifancev
b6d7753a80 Срез 0 [7.5]: M-4 — валидация SCAN_SETTLE_SECONDS < SCAN_TIMEOUT_SECONDS
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 16:04:19 +04:00
Andrey Epifancev
8c3c06266c Срез 0 [7.5]: M-2 — склейка и сериализация ответа в потоке, сбой сериализации как сбой записи raw (закрывает T3.1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 16:04:19 +04:00
Andrey Epifancev
5b16a53a99 Срез 0 [7.5]: M-1 — TIMEOUT при чередующихся ошибках опроса несёт описание последней ошибки (R-39, design #80)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 16:02:48 +04:00
Andrey Epifancev
b127c02422 Срез 0 [7.5]: финальная волна, документы — T5.1, T5.3, T6.1, T6.3, T6.6, M-3
- README: верхняя граница задержки settle, шаг 5 «Полного круга»; критерии ошибочного файла (корень не объект)
- CLAUDE.md: по одному json на каждый распознанный документ; критерии ошибочного файла
- design.md: D-5 — оговорка про нестроковый error.code отдельным предложением; №79 — источник указания, SQL уборки в порядке FK
- open-questions-proposal.md п.20: замены ../docs/dev/README.md (185–187, 224–226, 252–253) и комментарий docker-compose.yml:184
- план: Task 6 — пометка про замену down -v (R-27)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 16:01:13 +04:00
Andrey Epifancev
ed5622374e Срез 0 [7.5]: сквозная проверка в реальном compose — aep_3/aep_6/aep_5.PDF прошли; R-27, R-37, R-38 (design #77–79)
Профиль services (arm-core, scan-gateway из этого worktree, scan-emulator): aep_3.pdf → error NOT_IMPLEMENTED
с metrics.warnings про <job_id>_ofr.json, aep_6.pdf → error NOT_IMPLEMENTED без metrics, aep_5.PDF →
error INVALID_DOCUMENT; raw/<job_id>.json — склейка набора, тела совпадают с фикстурами; Tmp пуст, .part нет.
Compose правки не требует (умолчания Tmp/settle 2 с). Устаревшие места ../docs/dev/README.md — точными
заменами в open-questions-proposal.md, пункт 20. Уборка — rm -sfv своих сервисов и том hotfolder вместо
down -v (R-27). Отметки: tasks 7.5, план Task 6.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 15:30:46 +04:00
Andrey Epifancev
877541db10 Срез 0 [7.4]: README и CLAUDE.md — набор ответа, Tmp, settle, склеенный raw; уточнение D-5, отметки 7.4
Раскладка Output/<job_id>/*.json (склеиваются все файлы), SCAN_TMP_DIR, SCAN_SETTLE_SECONDS, структура raw/{job_id}.json,
успех = error.code == "", ошибочные файлы в metrics.warnings, правило набора эмулятора. D-5: не строковый error.code
тоже ошибка (UNKNOWN_ERROR, R-32). Решения R-36 (design #76). Тест TestAnswerSet на реальных фикстурах помечен fixtures.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 15:08:07 +04:00
Andrey Epifancev
68389d5cc2 Срез 0 [7.4]: интеграционные тесты — наборы ответа ИС на реальных фикстурах (aep_3/aep_6/aep_5.PDF)
Сквозные сценарии с реальными NATS и MinIO: aep_3 (два файла, warnings про _ofr.json, склеенный raw),
aep_6 (NOT_IMPLEMENTED без metrics), aep_5.PDF (INVALID_DOCUMENT, повтор запроса), unknown (заглушка эмулятора).
settle 0.2 с, SCAN_TMP_DIR передаётся эмулятору. Исправлено устаревшее ожидание data_ocr в сценарии unknown (R-28).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 15:04:30 +04:00
Andrey Epifancev
24af6f1b9a Срез 0 [7.3]: эмулятор — набор файлов ответа на задание
Для `<uuid>_<name>.<ext>` эмулятор собирает все `<name>.json` и `<name>_*.json` из SCAN_FIXTURES_DIR (не префикс
`<name>*`) и кладёт их в `Output/<uuid>/` как `<uuid>.json` / `<uuid>_<suffix>.json` с `file_uid = <uuid>`. Набор
пишется в `<root>/<SCAN_TMP_DIR>/.<uuid>.answer` и переименовывается в каталог ответа целиком (R-26). Ответ «нет пары» —
`data_ocr` объектом с пустыми полями (R-28). Решения исполнителя — design №75 (R-35).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:48:57 +04:00
Andrey Epifancev
2a0d0a8939 Срез 0 [7.2]: спека и design — R-29 (settle от первого опроса), D-5 с warnings, решения №67–74
- spec «Ответ из одного файла»: набор передаётся не позднее SCAN_SETTLE_SECONDS +
  SCAN_POLL_SECONDS после первого опроса, увидевшего окончательный набор (R-29).
- design D-5: сигнатура NormalizeOutcome с warnings; воркер дописывает их в metrics.warnings.
- Decisions made without discussion: №67 R-29, №68 решения исполнителя Task 2, №69–72
  R-30..R-33 (предел warnings, склейка в src/gateway/merge.py, крайние случаи NullNormalizer,
  слияние warnings с metrics), №73 T2.1/T2.2/T2.4, №74 R-34 (адаптация test_roundtrip.py).
- Отметки: Task 3 в плане, 7.2 в tasks.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:28:26 +04:00
Andrey Epifancev
973cf2717f Срез 0 [7.2]: склейка ответа ИС, сырой raw/ = склейка, NullNormalizer по файлам, metrics.warnings
- Исправлена критическая ошибка: NullNormalizer считал ответ ошибкой при `error is not None`,
  а у реального успеха ИС error = {"code": "", "message": ""} — aep_3.json и aep_6.json давали
  UNKNOWN_ERROR. Успех файла теперь — error.code == "" (отсутствующий/null/не-объектный error
  и error без кода ошибкой не считаются); регрессия — на реальных фикстурах (маркер fixtures).
- src/gateway/merge.py: merge_answers(job_id, files) -> MergedResponse
  {"job_id", "files": [{"name", "response", "parse_error"}]} по возрастанию имени; не JSON —
  parse_error файла; dump_merged — UTF-8 (с ASCII-экранированием при одиночном суррогате).
- Воркер: опрос отдаёт готовый набор → склейка → raw/{job_id}.json (application/json) до
  нормализации → Normalizer.normalize(склейка). Перечитывание «не JSON» убрано (R-24).
- NullNormalizer: все файлы ошибочны — код/сообщение первого по имени (parse_error и
  не-объект — INVALID_FORMAT, код вне контракта — UNKNOWN_ERROR с исходным кодом); есть
  успешный — NOT_IMPLEMENTED; ошибочные файлы — строки `<имя>: <код ИС>: <сообщение>`.
- NormalizeOutcome.warnings (R-25) → metrics.warnings конверта и файла результата; пустые
  опускаются; не больше 100 строк по 1000 символов, лишние — сводной строкой (R-30).
- Тесты: test_merge.py, test_normalizer.py переписан под склейку, test_worker.py — склейка в
  raw, ошибка одного файла, parse_error в warnings, все ошибочны, реальные aep_3+aep_3_ofr и
  aep_6, settle и таймаут ненастоявшегося набора; SUCCESS_ANSWER — в форме реального успеха.
  Интеграционный test_roundtrip.py минимально переведён на формат склейки (прогон — Task 5).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:27:07 +04:00
Andrey Epifancev
83e55af941 Срез 0 [7.2, fix T2.1 T2.2 T2.4]: горячие папки — повторный листинг после чтения, ENOTDIR в SMB
- T2.1: после чтения набора каталог ответа листается заново (Local и SMB); если снимок (имена и
  размеры) отличается от настоявшегося — None, набор настаивается заново.
- T2.2: SmbHotFolder — ENOTDIR из листинга Output/<job_id> (на месте каталога файл) — None,
  как у LocalHotFolder.
- T2.4: тест SMB — ENOENT / SHARING_VIOLATION / ENOTDIR во время настаивания сбрасывают
  накопленное состояние settle (pending_jobs() == 0).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:18:29 +04:00
Andrey Epifancev
a0ab13d3d9 docs: поправка человека — убрана формулировка про части одного документа; в одном json несколько таблиц разных форм
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:13:32 +04:00
Andrey Epifancev
ee4359d535 Срез 0 [7.1]: отметка выполнения Task 2 в плане и задачи 7.1 в tasks.md
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:01:39 +04:00
Andrey Epifancev
e8453159e8 Срез 0 [7.1]: горячие папки — набор *.json ответа, готовность по settle, служебный каталог Tmp
- HotFolder.poll(job_id) -> list[AnswerFile] | None: все *.json каталога <output>/<job_id>/ по возрастанию
  имени, когда набор не пуст и имена/размеры не менялись SCAN_SETTLE_SECONDS (SettleTracker: состояние по
  job_id, внедряемые часы, сброс при выдаче/опустении, предел 1024 задания, потокобезопасно);
- put: временный .<name>.part в <корень>/<SCAN_TMP_DIR>/ (создаётся), rename в Input (Local и SMB);
- настройки SCAN_TMP_DIR (Tmp) и SCAN_SETTLE_SECONDS (2, >= 0); в .env.test settle = 0 (R-23);
- воркер: переходная прослойка — берётся первый файл набора (склейка всех — Task 3, R-22).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 14:01:39 +04:00
Andrey Epifancev
52c032a308 Срез 0 [fix T1.1-T1.11]: согласованность артефактов change после решений человека
Заменённые решения помечены в design.md (№32, 35, 43, 48), добавлены №63-66 (R-22, R-23, R-25, R-28),
маркеры R-n (design #m); уточнены правила результата (UNKNOWN_ERROR, не-объект, коды в warnings),
сценарии таймаута по settle, имена файлов <job_id>*.json, ответ эмулятора «нет пары» с data_ocr-объектом.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 13:46:09 +04:00
Andrey Epifancev
65ae419ee5 docs: поправка человека — соответствие «таблица ↔ документ» не взаимно однозначно (план, предложение по OQ-77)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 13:39:16 +04:00
Andrey Epifancev
5dbb4dab20 Срез 0 [7.0]: change обновлён под решения человека 04.10.2026
Склейка всех json каталога ответа (settle), SCAN_TMP_DIR вне Input, успех = error.code == "",
per-file ошибки в metrics.warnings, набор файлов в эмуляторе; решения №54-62 в design.md;
OQ-76/77/78/79 отмечены решёнными; группа 7 в tasks.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 13:38:48 +04:00
Andrey Epifancev
0ef69a1fda chore: план доработки scan-gateway по реальным ответам ИС (несколько json на задание, служебный каталог)
Решения человека 04.10.2026 (OQ-76/77/78/79) и факты о формате ответа по новым фикстурам aep_3 и aep_6:
data_ocr — объект, успех = error.code == "", таблиц в одном json несколько.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-04 13:32:42 +04:00
Andrey Epifancev
842f7a69e4 docs: README про усечение длинных имён, решения финальной волны (R-21, C-1, I-1, T12.1, T7.3) в design.md
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:14:14 +04:00
Andrey Epifancev
d91c025f73 Срез 0 [fix proposal]: предложения для docs дополнены итогами финального ревью
open-questions-proposal.md: SCAN_GATEWAY_PORT=8002 для `../docs/dev` (T16.1); проверки после применения
compose-фрагмента — `up -d --wait scan-gateway scan-emulator` с кодом 0 и строки лога эмулятора в формате `run()`
(T16.2, I-2); пометка, что фрагмент рабочий только после C-1 (проверено `docker run`, код 2); I-3 — replay
с `attempt + 1` для задания из LRU; `make up` в tabular-parser без `minio-init` (только сообщить); в «Что осталось
человеку» — не архивировать до проверки фрагмента, решение по I-3, приоритет T12.5.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:06:28 +04:00
Andrey Epifancev
17e334f556 Срез 0 [fix docs]: README, CLAUDE.md и design.md #32 сверены с кодом
README: настройки шлюза читаются при импорте `src.app`, пакет `src` и эмулятор их не читают (после C-1);
`data_ocr` реальных фикстур — объект с пустыми полями и `table_structure: []`; непустой `data_ocr` в `ok_doc.json` —
требование тестовой фикстуры, не NullNormalizer; в «Ограничения Среза 0» — зависший вызов SMB и поздний TIMEOUT
(T12.2/T8.4), перечитывание недописанного ответа (T3.5), битая фикстура эмулятора.
CLAUDE.md: TIMEOUT по `min(reply_deadline, старт + SCAN_TIMEOUT_SECONDS=300)`; заглушки шаблона перечислены.
design.md, строка 32: формулировка про `data_ocr` реальных фикстур.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:05:42 +04:00
Andrey Epifancev
ee272b2b92 Срез 0 [fix T2.2]: удалён мёртвый _normalize_root_path из app.py
Функция осталась от шаблона и нигде не вызывается (grep по api/, включая тесты).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:04:07 +04:00
Andrey Epifancev
ef22e3b075 Срез 0 [fix T7.3, T7.5]: inbox_name укорачивает длинные имена; сбой удаления .part не маскирует ошибку
T7.3: имя из 124+ кириллических символов давало `.part` длиннее 255 байт -> ENAMETOOLONG на ext4 и SMB/NTFS
-> ложный RECOGNITION_FAILED. `inbox_name` обрезает основу по code points так, чтобы `.<job_id>_<имя>.part`
занимал не больше 240 байт UTF-8, сохраняя расширение (до 16 символов после последней точки); короткие имена не
меняются, ValueError — по-прежнему только для пустого имени, `.` и `..`.
T7.5: `LocalHotFolder.put` подавляет OSError при удалении `.part` (`contextlib.suppress`), наружу выходит исходное
исключение; в `smb.py` это уже было.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:04:00 +04:00
Andrey Epifancev
d288d19622 Срез 0 [fix T12.4]: тест суррогата в original_name назван по фактическому поведению
Запрос с одиночным суррогатом в `original_name` отвергается схемой (pydantic `string_unicode`), а не «не ломает
публикацию»: тест переименован в `..._is_schema_mismatch` и проверяет код SCHEMA_MISMATCH, отсутствие ack и `put`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:02:43 +04:00
Andrey Epifancev
0420c85219 Срез 0 [fix T12.1]: SCHEMA_MISMATCH для задания в работе/обработанного не публикуется
Конверт `result:<job_id>:<attempt>` битого запроса совпадал по Nats-Msg-Id с настоящим результатом, и ядро
(дедуп по result_msg_id) отбросило бы настоящий итог. `_reject` нормализует job_id (`uuid.UUID`, нижний регистр)
и при `_seen` только пишет предупреждение. Тест: битый запрос с тем же job_id во время ожидания ИС и после итога,
в том числе UUID в верхнем регистре — опубликованы только ack и настоящий итог.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:02:28 +04:00
Andrey Epifancev
38f67bc582 Срез 0 [fix I-1]: make up создаёт бакет (run --rm minio-init)
На свежем томе `up -d --wait postgres nats minio` не запускал одноразовый `minio-init`, и интеграционные тесты падали
на NoSuchBucket. Добавлен шаг `docker compose run --rm minio-init` (идемпотентно, `--ignore-existing`, код выхода
пробрасывается); комментарий и help цели поправлены.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:01:39 +04:00
Andrey Epifancev
ad3fb34dba Срез 0 [fix C-1]: пакет src не создаёт приложение — эмулятор стартует без настроек шлюза
`api/src/__init__.py` оставлен только с docstring: `python -m src.scan_emulator` больше не импортирует `src.app`
и не требует `OPENBAO__*`. `run_server.py` импортирует `app` из `src.app`, `get_settings` — из `src.core.config`.
Регрессионный тест: subprocess эмулятора с окружением только PATH/ENV_FILE/SCAN_HOTFOLDER_PATH -> код 2,
сообщение про корень горячей папки, без ValidationError.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 05:01:16 +04:00
Andrey Epifancev
58bcae2a57 Срез 0 [6.3]: прогон DoD, предложения для docs и правки .env.example
- open-questions-proposal.md: compose-фрагмент scan-emulator/scan-gateway, таблица предложений по контракту и OQ,
  чеклист для человека (файлы в ../docs не менялись)
- .env.example: SCAN_EMULATOR_POLL_SECONDS; уточнён комментарий PUBLISH_RETRIES (повторы после первой попытки)
- отмечена задача 16 плана и пункт 6.3 tasks.md

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:44:33 +04:00
Andrey Epifancev
27fe24cb5e Срез 0 [6.2]: make check-full = check + test-integration + docker-build
Добавлена цель docker-build (IMAGE ?= scan-gateway:dev, контекст — корень репозитория).
check-full вызывает шаги последовательно через $(MAKE), поэтому порядок и остановка на
первой ошибке гарантированы и при make -j. README/CLAUDE.md и help приведены в соответствие,
отмечены задача 15 плана и пункт 6.2 tasks.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:38:16 +04:00
Andrey Epifancev
e2286354c9 docs: D-9 design.md приведён к R-7/R-10 (интеграционный тест в процессе, синтетический успех)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:33:00 +04:00
Andrey Epifancev
22f62a4eda Срез 0 [1.3]: README.md и актуализированный CLAUDE.md, отметка задачи
README.md описывает сервис как он есть: границы (без БД и фронта), поток обработки,
протокол горячих папок, переменные окружения шлюза и эмулятора, коды ошибок,
контракт NATS, запуск (dev, полный круг против compose и эмулятора, проверки),
/healthcheck, Docker-образ и ограничения Среза 0. CLAUDE.md: «Соглашения шаблона»
переписаны по фактическому дереву api/, устаревший SCAN_EMULATOR=1 заменён на
`make emulator` / `python -m src.scan_emulator`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:31:54 +04:00
Andrey Epifancev
700aba4dc3 docs: решения исполнителя задачи 6.1 в design.md slice-0-skeleton
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:28:36 +04:00
Andrey Epifancev
6552ebe345 Срез 0 [6.1]: сквозной интеграционный тест test_roundtrip.py, отметка задачи
Lifespan приложения и эмулятор ИС в процессе теста (R-7) против compose NATS/MinIO, горячая папка в tmp-каталоге.
Три сценария (R-10): aep_5.pdf (реальная фикстура) — INVALID_DOCUMENT от ИС, повтор во время ожидания и после
результата даёт ещё один ack без второго файла; ok_doc.pdf (синтетический успех) — NOT_IMPLEMENTED; unknown.pdf —
INVALID_DOCUMENT эмулятора. Проверяются схема конверта, service.name, Nats-Msg-Id, raw/<job_id>.json байт в байт,
входящий файл против объекта S3, /healthcheck, остановка без незавершённых задач и ошибок в логе.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-04 04:23:10 +04:00