From ed5622374ee917220d70ea5901e9a3e244d64f73 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sun, 4 Oct 2026 15:30:46 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A1=D1=80=D0=B5=D0=B7=200=20[7.5]:=20=D1=81?= =?UTF-8?q?=D0=BA=D0=B2=D0=BE=D0=B7=D0=BD=D0=B0=D1=8F=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20=D0=B2=20=D1=80=D0=B5=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D0=BC=20compose=20=E2=80=94=20aep=5F3/?= =?UTF-8?q?aep=5F6/aep=5F5.PDF=20=D0=BF=D1=80=D0=BE=D1=88=D0=BB=D0=B8;=20R?= =?UTF-8?q?-27,=20R-37,=20R-38=20(design=20#77=E2=80=9379)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Профиль services (arm-core, scan-gateway из этого worktree, scan-emulator): aep_3.pdf → error NOT_IMPLEMENTED с metrics.warnings про _ofr.json, aep_6.pdf → error NOT_IMPLEMENTED без metrics, aep_5.PDF → error INVALID_DOCUMENT; raw/.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 --- docs/superpowers/plans/2026-10-04-multi-file-response.md | 2 +- openspec/changes/slice-0-skeleton/design.md | 3 +++ openspec/changes/slice-0-skeleton/open-questions-proposal.md | 3 +++ openspec/changes/slice-0-skeleton/tasks.md | 5 +++-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/plans/2026-10-04-multi-file-response.md b/docs/superpowers/plans/2026-10-04-multi-file-response.md index 85ea50f..24542f9 100644 --- a/docs/superpowers/plans/2026-10-04-multi-file-response.md +++ b/docs/superpowers/plans/2026-10-04-multi-file-response.md @@ -53,4 +53,4 @@ ### Task 6: [R6] Проверка в реальном compose -- [ ] Без правок `../docs`: `docker compose -f ../docs/dev/docker-compose.yml --profile services up -d --build` нужных сервисов (`scan-gateway`, `scan-emulator` + зависимости; `--wait` по именам сервисов, не голый), затем сценарии через реальный compose: положить `aep_3.pdf`, `aep_6.pdf`, `aep_5.PDF` из `../fixtures/ocr` в MinIO и вставить задания способом из `../docs/dev/README.md` («Проверка окружения»); зафиксировать выводы (в т.ч. содержимое `raw/.json`) в отчёт. В конце окружение остановить (`down -v`). Если `../docs/dev/docker-compose.yml` требует правки (например, переменных `SCAN_TMP_DIR`/`SCAN_SETTLE_SECONDS`) — не править, а записать точный фрагмент в `open-questions-proposal.md`. +- [x] Без правок `../docs`: `docker compose -f ../docs/dev/docker-compose.yml --profile services up -d --build` нужных сервисов (`scan-gateway`, `scan-emulator` + зависимости; `--wait` по именам сервисов, не голый), затем сценарии через реальный compose: положить `aep_3.pdf`, `aep_6.pdf`, `aep_5.PDF` из `../fixtures/ocr` в MinIO и вставить задания способом из `../docs/dev/README.md` («Проверка окружения»); зафиксировать выводы (в т.ч. содержимое `raw/.json`) в отчёт. В конце окружение остановить (`down -v`). Если `../docs/dev/docker-compose.yml` требует правки (например, переменных `SCAN_TMP_DIR`/`SCAN_SETTLE_SECONDS`) — не править, а записать точный фрагмент в `open-questions-proposal.md`. diff --git a/openspec/changes/slice-0-skeleton/design.md b/openspec/changes/slice-0-skeleton/design.md index dc0582f..e4af381 100644 --- a/openspec/changes/slice-0-skeleton/design.md +++ b/openspec/changes/slice-0-skeleton/design.md @@ -212,3 +212,6 @@ warnings; `aep_5.PDF` → `INVALID_DOCUMENT` с `raw_s3_key`; объект `raw/ | 74 | 2026-10-04 | R-34 (design #74): интеграционный `tests/integration/test_roundtrip.py` минимально переведён на формат склейки `raw/` и успех `error = {"code": "", "message": ""}` (без новых сценариев — они в 7.4) | иначе он проверял бы старый формат `raw/`; полноценная переработка — задача 7.4 | не прогнан: в поднятом compose нет бакета `arm-ka` (`NoSuchBucket`), проверка — 7.4 после `make up` | | 75 | 2026-10-04 | R-35 (design #75): решения исполнителя Task 4 (7.3): эмулятор читает `SCAN_TMP_DIR` (по умолчанию `Tmp`) через `EmulatorSettings.tmp_dir`, как шлюз, не завися от настроек шлюза; набор собирается в `//..answer` и целиком переименовывается в `Output//`; остаток прерванной сборки удаляется, ответ с тем же uuid, уже лежащий в `Output`, заменяется (rmtree + rename); повреждённая фикстура набора даёт ответ-ошибку `INVALID_DOCUMENT` на своём месте, остальные файлы набора остаются; фикстуры с одним `suffix` (`a.JSON`/`a.json`) — берётся первая по имени; `aep_8.pdf` теперь даёт `_form4.json`, а не `.json` (прежнее правило `*`) | spec scan-emulator: набор появляется атомарно, rename каталога возможен только в пределах одной ФС, служебная папка корня уже есть у шлюза (`Tmp`) | повторная публикация uuid на миг убирает каталог; потребители, ждавшие `aep_8` как `.json`, должны брать набор целиком | | 76 | 2026-10-04 | R-36 (design #76): решения исполнителя Task 5 (7.4): интеграционный `test_roundtrip.py` с `SCAN_SETTLE_SECONDS=0.2` и `SCAN_POLL_SECONDS=0.1` (`.env.test` держит settle = 0 для остальных тестов); эмулятору теста отдаются копии только `aep_3.json`, `aep_3_ofr.json`, `aep_5.json`, `aep_6.json` и синтетический `ok_doc.json` (не весь `fixtures/ocr`); сценарий `aep_5` берёт реальное имя `aep_5.PDF` (расширение в верхнем регистре) и проверяет, что единственный ошибочный файл попадает и в итоговую ошибку, и в `metrics.warnings` (D-5); проверка `raw/` общая для всех сценариев: имена файлов склейки = все `*.json` каталога ответа по возрастанию, тела совпадают с файлами; в сценарии `unknown` устаревшее ожидание `data_ocr = []` заменено на объект с `table_structure: []` (R-28) — расхождение обнаружено первым прогоном (в Task 4 тест не запускался, №74); T3.3: D-5 уточнён — не строковый, не `null`/`""` `error.code` (число, список, `true`) — тоже ошибка файла с `UNKNOWN_ERROR` (R-32); T4.1: сквозной тест `TestAnswerSet` на реальных фикстурах помечен `fixtures` | тест должен проверять то, что есть в коде и спеке, а не прежнюю форму ответа; settle 0.2 с не удлиняет тест заметно; полная копия `fixtures/ocr` подтянула бы лишние наборы | тесты `fixtures` пропускаются без репозитория `../fixtures`; интеграционные тесты делят NATS и queue group `scan-gateway` с другими экземплярами шлюза | +| 77 | 2026-10-04 | R-27 (design #77): уборка после сквозной проверки Task 6 (7.5) — `docker compose -f ../docs/dev/docker-compose.yml --project-name arm-ka --profile services rm -sfv` только своих сервисов (`scan-gateway`, `scan-emulator`, `arm-core`, `arm-core-migrate`, одноразовый `minio-init`) и удаление тома `arm-ka_hotfolder` после проверки, что его не использует ни один контейнер, вместо `down -v` из плана; `postgres`/`nats`/`minio` и их тома не тронуты | проект compose `arm-ka` общий для всех worktree: `down -v` удалил бы и тома `postgres-data`/`minio-data` с данными другой работы | человек хотел полный сброс — одна команда `docker compose --profile services down -v` из `docs/dev` | +| 78 | 2026-10-04 | R-37 (design #78): состав и сборка сервисов в Task 6 (7.5): `up -d --build arm-core scan-gateway scan-emulator`, затем `up -d --wait` по тем же именам; `tabular-parser` не поднимался (конвейер `image` его не использует); контекст сборки `scan-gateway` в compose — `../../scan-gateway` от `docs/dev` worktree, т. е. сам worktree ветки (`.wt/slice-0/scan-gateway`), поэтому отдельный `make docker-build` не нужен; `arm-core:dev` пересобран из worktree `arm-core` (кэш, ID образа не изменился) | проверяется шлюз; лишний сервис только добавил бы шум на шине; правка compose не требовалась | если `tabular-parser` нужен для проверки чего-то ещё — отдельный прогон | +| 79 | 2026-10-04 | R-38 (design #79): после Task 6 тестовые строки БД оставлены (`legal_entity` ИНН `7700000001`, четыре `s3_object`/`stored_file`/`recognition_job` — id в отчёте Task 6), а объекты `uploads/…` и `raw/…` в MinIO удалены (`mc rm` поимённо); строки `s3_object` поэтому указывают на несуществующие ключи | так велел бриф (строки БД — оставить, объекты MinIO — удалить); бакет до прогона был пуст | чтение этих `s3_object` даст `NoSuchKey`; убрать — четыре `delete` по id или полный сброс | diff --git a/openspec/changes/slice-0-skeleton/open-questions-proposal.md b/openspec/changes/slice-0-skeleton/open-questions-proposal.md index ca3610e..d9d3548 100644 --- a/openspec/changes/slice-0-skeleton/open-questions-proposal.md +++ b/openspec/changes/slice-0-skeleton/open-questions-proposal.md @@ -135,6 +135,7 @@ python -m src.scan_emulator` завершается кодом 2 с сообще | 17 | Replay с `attempt + 1` для `job_id`, который есть в LRU процесса, получает только ack и не обрабатывается (R-17, spec «обработан этим процессом»); по `../docs/context/03-architecture.md` replay — «сброс в `queued` с `attempt + 1`». На единственном инстансе такое задание повисает до `reply_deadline` -> `timeout` | I-3, R-17 | При одобрении: LRU `_done` с ключом `(job_id, attempt)` — более высокий `attempt` готового задания обрабатывать заново; `_active` — по `job_id` (повтор во время обработки — только ack). Решает человек | | 18 | `make up` в tabular-parser (`../tabular-parser/Makefile`, цель `up`) тоже не запускает `minio-init`: на свежем томе бакета нет | I-1 | Чужой репозиторий — только сообщить владельцу; в scan-gateway исправлено (`run --rm minio-init` после `up -d --wait`) | | 19 | Расхождения и наблюдения контроллера при исполнении (`Observation` в `progress.md`) | `progress.md` | См. ниже | +| 20 | Сквозная проверка 7.5 (Task 6, 04.10.2026) на профиле `services`: `aep_3.pdf` → `error NOT_IMPLEMENTED` + `metrics.warnings` про `_ofr.json`, `aep_6.pdf` → `error NOT_IMPLEMENTED` без `metrics`, `aep_5.PDF` → `error INVALID_DOCUMENT` + одна строка `warnings`; **`../docs/dev/docker-compose.yml` правки не требует**: умолчания `SCAN_TMP_DIR=Tmp` и `SCAN_SETTLE_SECONDS=2` подходят (`Tmp` создаётся шлюзом в томе `hotfolder`, после прогона пуст). Устарел раздел «Проверка окружения» `../docs/dev/README.md` (сценарий 2 и абзац «не покрывает») | Task 6, R-37 (design #78) | Владельцу `docs`, в `../docs/dev/README.md`: (1) строки 220–221 заменить на `# [2026-10-04 11:23:58 UTC] 600B STANDARD 0ea16933-4cc1-4b50-bda9-a97089a918c5.json` и `dmc cat "local/arm-ka/raw/$JOB.json" # {"job_id": "", "files": [{"name": ".json", "response": {"file_uid": "", "error": {"code": "INVALID_DOCUMENT", "message": "Не удалось определить тип документа."}, "data_ocr": {…}}, "parse_error": null}]}`; (2) в строке 226 лог эмулятора — `_aep_5.PDF -> /data/hotfolder/Output/` (каталог набора, не файл); (3) строки 227–229 («успешный путь image … не проходит — нужен успешный ответ ИС») заменить на: «Успешные ответы ИС есть (`aep_3.json` + `aep_3_ofr.json`, `aep_6.json`); шлюз склеивает все `*.json` каталога ответа и отвечает `NOT_IMPLEMENTED` (нормализация — Срез 3), ошибочные файлы набора — в `metrics.warnings`»; (4) строку 230 заменить на «`processing` виден ~10 с: задержка эмулятора 2 с, затем до `SCAN_POLL_SECONDS + SCAN_SETTLE_SECONDS` (5 + 2 с) после первого опроса, увидевшего набор»; (5) в строке 255 «у ошибок `scan-gateway` `recognition_job.metrics` пустой» заменить на «у ошибок `scan-gateway` в `recognition_job.metrics` только `warnings` — по строке `<имя файла>: <код>: <сообщение>` на ошибочный файл ответа ИС, если такие есть»; (6) после сценария 2 добавить сценарий «2а. `image` → успешный ответ ИС → `NOT_IMPLEMENTED`»: `new_job image PDF ocr/aep_3.pdf && poll 60` → `# 11:22:07 queued` / `# 11:22:08 processing` / `# 11:22:18 error NOT_IMPLEMENTED`; `dpsql -c "select status, error_code, metrics from dkb.recognition_job where id = '$JOB'"` → `# error | NOT_IMPLEMENTED | {"warnings": ["_ofr.json: INVALID_DOCUMENT: Не удалось определить тип документа."]}`; `new_job image PDF ocr/aep_6.pdf && poll 60` → `error NOT_IMPLEMENTED`, `metrics` пусто; (7) в той же строке 255 «полная очистка — `docker compose --profile services down -v`» дополнить оговоркой, что это удаляет и общие тома `postgres-data`/`minio-data` всех worktree (R-27, design #77) | Строки `Observation` из `progress.md` (только пересказ, статусы — в ledger): @@ -163,6 +164,8 @@ python -m src.scan_emulator` завершается кодом 2 с сообще - [ ] После merge: `openspec archive slice-0-skeleton`, коммит и push архива в main (чеклист закрытия). - [ ] Перенести compose-фрагмент выше в `../docs/dev/docker-compose.yml` (и обновить `README.md`, пункт 10) — отдельным PR репозитория `docs`. +- [ ] Перенести правки раздела «Проверка окружения» `../docs/dev/README.md` по итогам сквозной проверки 7.5 (пункт 20) — + тем же PR репозитория `docs`; compose правки не требует. - [ ] Согласовать с владельцем tabular-parser семантику `PUBLISH_RETRIES` (пункт 1) и решить судьбу правила про `SCHEMA_MISMATCH` + ack (пункт 2). - [x] Пункты 3-6 (OQ-76/77/78/79: образцы успешных ответов, формат `data_ocr`, состав файлов ответа, временный файл) diff --git a/openspec/changes/slice-0-skeleton/tasks.md b/openspec/changes/slice-0-skeleton/tasks.md index be5aa9e..e64d60b 100644 --- a/openspec/changes/slice-0-skeleton/tasks.md +++ b/openspec/changes/slice-0-skeleton/tasks.md @@ -103,7 +103,8 @@ design №54–66. Контракт v1.0 не меняется; нормализ `_ofr.json` и двумя файлами в `raw/`, `aep_6.pdf` → `NOT_IMPLEMENTED` без warnings, `aep_5.PDF` → `INVALID_DOCUMENT`; `README.md`, `CLAUDE.md`, `.env.example`, `.env.test` актуализированы (`SCAN_TMP_DIR`, `SCAN_SETTLE_SECONDS`, склеенный raw); проверка: `make check-full` (при поднятом `make up`) — exit 0, вывод в отчёт -- [ ] 7.5 Проверка в реальном compose (без правок `../docs`): сервисы `scan-gateway` и `scan-emulator` собираются и стартуют, +- [x] 7.5 Проверка в реальном compose (без правок `../docs`): сервисы `scan-gateway` и `scan-emulator` собираются и стартуют, сценарии `aep_3.pdf`, `aep_6.pdf`, `aep_5.PDF` проходят способом из `../docs/dev/README.md`, содержимое - `raw/.json` зафиксировано; окружение остановлено (`down -v`); необходимые правки compose (`SCAN_TMP_DIR`, + `raw/.json` зафиксировано; окружение остановлено (`down -v`; по R-27, design #77 — `rm -sfv` своих сервисов и том + `hotfolder`, общие `postgres`/`nats`/`minio` не тронуты); необходимые правки compose (`SCAN_TMP_DIR`, `SCAN_SETTLE_SECONDS`) — точным фрагментом в `open-questions-proposal.md`; проверка: отчёт со снимками выводов