# Предложения по открытым вопросам и контракту (из реализации `slice-0-skeleton`) Файлы `../docs` этот репозиторий не меняет: здесь — предложения для `docs/context/07-open-questions.md` и `docs/contracts/`; решение принимает человек / контроллер docs. ## P-1. Невалидный запрос без читаемых `job_id` / `file_id` / `attempt` (задача 4.3) **Что видно в коде.** D-5 и spec требуют на запрос, не прошедший схему, ответить конвертом `error SCHEMA_MISMATCH` «если `job_id` читаем». Но `recognition-result-message.schema.json` требует ещё `file_id`, а `Nats-Msg-Id` результата — `result::`. Если хоть одно из трёх не читается (тело не JSON, не объект, `job_id`/`file_id` не uuid, `attempt` нет ни в теле, ни в заголовке запроса `Nats-Msg-Id = :`), валидный конверт собрать нельзя. **Как сделано сейчас.** Значения не выдумываются: запрос логируется уровнем error (`error_category=schema_mismatch`, причина, размер тела) и отбрасывается без ack и без результата. Ядро без ack повторит запрос `OUTBOX_MAX_REPUBLISH` раз и переведёт задание в `dlq` — то есть «молчание» сервиса превращается в `dlq` на стороне ядра. `attempt` при невалидном значении в теле берётся из заголовка запроса. **Предложение.** Записать это в контракт явно (раздел «Сообщение не по схеме»): «если `job_id`, `file_id` или `attempt` не читаемы, сервис логирует ошибку и не отвечает; ядро обнаружит задание по `dlq`/`timeout`». Альтернатива — сделать `file_id` необязательным в конверте `error` (MINOR 1.1) — выглядит избыточной: ядро всегда шлёт корректные идентификаторы, нечитаемое тело — это сбой транспорта, а не данных. ## P-2. Противоречие: ack при несовместимой версии запроса (`schema_version = "2.0"`) - `docs/openspec/changes/slice-0-skeleton/specs/recognition-contract/spec.md`, сценарий «Несовместимая версия запроса»: «публикуется результат `SCHEMA_MISMATCH`, **запрос подтверждается (ack)**, чтобы не зациклить доставку». - `openspec/changes/slice-0-skeleton/specs/tabular-recognition-service/spec.md` (этот репозиторий), сценарий «Несовместимая версия»: «публикуется конверт `SCHEMA_MISMATCH`, **ack не публикуется**»; D-5 — так же. **Как сделано сейчас.** По спеке этого репозитория и брифу задачи: без ack, только результат `error SCHEMA_MISMATCH`. Зацикливания нет: ядро (`arm-core`, `recognition_exchange`) принимает результат и в статусе `published` («потерянный ack не блокирует результат») и закрывает задание, повторов не будет. **Предложение.** Привести сценарий контракта в docs к «ack не публикуется; результат `SCHEMA_MISMATCH` закрывает задание» (или наоборот — тогда правка одной ветки в `worker.py`). Нужно решение человека. ## P-3. Повторная попытка (`attempt + 1`) того же `job_id` после ошибки (задача 4.3) **Как сделано сейчас.** Дедупликация — по `job_id` (spec: «уже обрабатывается или обработан этим процессом»), любой итог (в том числе `error` и неудачная публикация результата) считается «обработан». Повторный запрос того же `job_id` с другим `attempt` в этом процессе получает только ack `ack::`, без обработки, пока `job_id` не вытеснен из LRU (`DONE_CACHE_SIZE`). Другая инстанция queue group обработает его как новый. **Вопрос.** Если ядро (Срез 3+, replay/«переотправить») будет повторять задание тем же `job_id` с `attempt + 1`, ключ дедупликации должен стать `(job_id, attempt)`. Предложение: зафиксировать в контракте, что новая попытка — это новый `attempt` и сервис обрабатывает её заново; тогда в воркере ключ LRU меняется на `(job_id, attempt)` (одна строка). Пока ядро повторяет только тем же `attempt` (повтор без ack), текущее поведение корректно. ## P-4. `docs/dev/README.md`: порт tabular-parser и копирование `.env.example` (прогон DoD, задача 5.2) - `../docs/dev/README.md` («Запуск сервисов из venv без Nexus») называет порт `tabular-parser` в профиле `services` 8001, а сервис по умолчанию слушает 8000 (`SERVER__PORT`, шаблон команды); там же советует `cp ../docs/dev/.env.example api/.env` — для этого сервиса избыточно (в нём нужны только `OPENBAO__MINIO__*`, `OPENBAO__NATS__NATS_URL`, `DEBUG`; свой `api/.env.example` лежит в репозитории). - **Предложение.** В docs либо задавать `SERVER__PORT=8001` в профиле `services` (переменная окружения контейнера), либо привести текст README к 8000; ожидаемые поля `/healthcheck` для `tabular-parser` — `uptime`, `version`, `nats`. Файлы в `../docs` этот change не меняет.