Срез 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>
This commit is contained in:
parent
ee272b2b92
commit
17e334f556
11
CLAUDE.md
11
CLAUDE.md
@ -6,7 +6,8 @@
|
||||
Адаптер внешней ИС Сканирование (Content AI) для PDF/PNG/JPG: переименовать файл `UUID_имя`, положить в «Входящие»,
|
||||
забрать `Исходящие/UUID/UUID.json`, сохранить сырой ответ в S3 `raw/{job_id}.json` (в БД не регистрируется),
|
||||
нормализовать в `../docs/contracts/recognition-result.schema.json` (`statementDocument`), опубликовать
|
||||
`recognition.result`. Таймаут 5 мин → `timeout`. **Нет доступа к БД.**
|
||||
`recognition.result`. Таймаут → ошибка `TIMEOUT` по `min(reply_deadline, старт + SCAN_TIMEOUT_SECONDS=300)`, старт —
|
||||
запись файла во «Входящие». **Нет доступа к БД.**
|
||||
|
||||
## Правила
|
||||
- Нормализация по фактическим ответам `../fixtures/ocr/aep_*.json`, а не по ТЗ (OQ-13, OQ-14): confidence → 0–100,
|
||||
@ -24,9 +25,11 @@
|
||||
|
||||
**Оставлено от шаблона:** `api/run_server.py` (точка входа платформы, `SERVER__HOST/PORT`), `api/src/app.py` (lifespan:
|
||||
SMB-сессия, S3, NATS, воркер; `GET /healthcheck`; gzip/proxy middleware), `core/config.py` (pydantic-settings,
|
||||
`OPENBAO__MINIO__*`, `OPENBAO__NATS__*`, `OPENBAO__SMB__*`, `DEBUG`), `core/logger.py`, `core/exception_handlers.py`,
|
||||
`common/exceptions.py`, `api/pytest.ini`, `api/requirements*.txt` (включая `smbprotocol`), `raisa_config.yml`,
|
||||
`setup/setup.sh` + `setup/pip.conf` (без npm-шага).
|
||||
`OPENBAO__MINIO__*`, `OPENBAO__NATS__*`, `OPENBAO__SMB__*`, `DEBUG`), `core/exception_handlers.py` (подключается в
|
||||
`create_app`), `api/pytest.ini`, `api/requirements*.txt` (включая `smbprotocol`), `raisa_config.yml`,
|
||||
`setup/setup.sh` + `setup/pip.conf` (без npm-шага). Заглушки шаблона без кода сервиса: `src/api/` (пустой `router`,
|
||||
пустые `dependencies/` и `endpoints/`), `src/schemas/` и `src/services/` (пустые пакеты), `common/exceptions.py`
|
||||
(`NotFoundError`, `CouldntCreateError` нигде не используются), `core/logger.py` (`setup_logger` нигде не вызывается).
|
||||
|
||||
**Вырезано (Срез 0):** `web/`, `setup/.npmrc`; из `app.py` — mount SPA, CORS, `raisa_fastapi_protected_api`; `db/`,
|
||||
`models/`, `repositories/`, `alembic/`; `OPENBAO.DATABASE`; из `requirements.txt` — `asyncpg`, `SQLAlchemy`, `alembic`,
|
||||
|
||||
21
README.md
21
README.md
@ -66,7 +66,10 @@ ack (с `attempt` повторного запроса); файл в ИС вто
|
||||
## Переменные окружения
|
||||
|
||||
Файл окружения — `ENV_FILE` (по умолчанию `.env`, путь от текущего каталога, то есть от `api/`); переменные окружения
|
||||
приоритетнее файла. Шаблон — `api/.env.example`. Настройки читаются при старте, не при импорте.
|
||||
приоритетнее файла. Шаблон — `api/.env.example`. Настройки шлюза читаются при импорте `src.app`: модуль создаёт `app`
|
||||
(это происходит при запуске `run_server.py` или `uvicorn src.app:app`). Импорт пакета `src` приложение не создаёт, поэтому
|
||||
эмулятор (`python -m src.scan_emulator`) настройки шлюза не читает: у него свои переменные (`SCAN_HOTFOLDER_PATH`,
|
||||
`SCAN_FIXTURES_DIR`, `SCAN_EMULATOR_*`) из окружения и `ENV_FILE`.
|
||||
|
||||
| Переменная | По умолчанию | Назначение |
|
||||
|---|---|---|
|
||||
@ -173,7 +176,9 @@ curl -s localhost:8000/healthcheck
|
||||
с сообщением ИС (обе реальные фикстуры — ответы-ошибки); для неизвестного имени — `INVALID_DOCUMENT` эмулятора.
|
||||
В бакете появится `raw/<job_id>.json`, `raw_s3_key` указан в конверте.
|
||||
6. Путь «успех -> `NOT_IMPLEMENTED`» требует успешной фикстуры: добавьте в `SCAN_FIXTURES_DIR` синтетический
|
||||
`<имя>.json` с `error: null` и непустым `data_ocr` (так делает `test_roundtrip.py`, фикстура `ok_doc.json`).
|
||||
`<имя>.json` с `error: null`. `NullNormalizer` содержимое `data_ocr` не смотрит; непустой `data_ocr` в фикстуре
|
||||
`ok_doc.json` из `test_roundtrip.py` — требование тестовой фикстуры (форма ответа ИС), а не поведения
|
||||
`NullNormalizer`.
|
||||
|
||||
### Проверки
|
||||
|
||||
@ -217,9 +222,17 @@ make check-full # check + test-integration + docker-build; перед PR
|
||||
|
||||
- `NullNormalizer` (`api/src/normalizer/`): ошибка ИС пробрасывается с её кодом, любой успешный ответ даёт
|
||||
`NOT_IMPLEMENTED`. Настоящая нормализация `data_ocr` в `statementDocument` — Срез 3.
|
||||
- Реальные фикстуры `aep_5.json` и `aep_8_form4.json` — ответы-ошибки ИС (`INVALID_DOCUMENT`, пустой `data_ocr`):
|
||||
успешных образцов нет, это открытый вопрос Среза 3.
|
||||
- Реальные фикстуры `aep_5.json` и `aep_8_form4.json` — ответы-ошибки ИС (`INVALID_DOCUMENT`; `data_ocr` — объект с
|
||||
пустыми полями (`form_name` пусто, `confidence 0.0`) и `table_structure: []`): успешных образцов нет, это открытый
|
||||
вопрос Среза 3.
|
||||
- Режим `smb` покрыт unit-тестами с подменённым `smbclient`; на реальной шаре ИС Сканирование не проверялся. Фактические
|
||||
имена каталогов и путь шары на стенде неизвестны — они настраиваются, а не зашиты в код.
|
||||
- Повторной отправки в ИС нет (OQ-15); падение шлюза после ack теряет задание до `reply_deadline` ядра.
|
||||
- Зависший вызов SMB (`poll`/`put`, до ~60 с по умолчанию `smbclient`) дедлайном воркера не прерывается: вызов идёт
|
||||
в потоке без собственного таймаута, поэтому `TIMEOUT` может прийти позже дедлайна на это время.
|
||||
- Недописанный ответ ИС: ответ не-JSON перечитывается через `SCAN_POLL_SECONDS` и принимается, когда стал JSON или
|
||||
перестал меняться между опросами; на дедлайне берётся последний прочитанный. Не-JSON ответ, принятый так, даёт
|
||||
`INVALID_FORMAT` (сырой ответ сохраняется в `raw/`).
|
||||
- Эмулятор: повреждённая фикстура (не JSON, не UTF-8 или корень не объект) не останавливает его — на такой файл
|
||||
он отвечает `INVALID_DOCUMENT` с сообщением о повреждённой фикстуре и пишет ошибку в лог.
|
||||
- Контейнер `scan-emulator` в compose отсутствует.
|
||||
|
||||
@ -133,7 +133,7 @@ D-7 и таймаут (с укороченными настройками). `Smb
|
||||
| 29 | 2026-10-04 | Задача 4.2: `inbox_name` заменяет символы, недопустимые в Windows/SMB (`: < > " \| ? *`, управляющие), на `_` и отбрасывает завершающие точки/пробелы; `put` остаётся строгим (T8.1) | допустимое имя пользователя (`Отчёт: 2024.pdf`) не должно проваливать задание; для ИС имя ничего не значит | имя во входящем каталоге отличается от загруженного |
|
||||
| 30 | 2026-10-04 | Задача 4.2: `SMB_PATH` — относительный (`share/dir`, склеивается с хостом) или полный UNC; учётные данные передаются в каждый вызов `smbclient`, если задан логин; `poll` возвращает `None` на `STATUS_SHARING_VIOLATION`/`STATUS_DELETE_PENDING`; монопольное открытие ответа сохранено; `mkdir` существующего `Input` с `ACCESS_DENIED` не роняет `put`, если каталог существует | повторяет вызовы старого `PDFWorker`; ИС может ещё писать ответ; урезанные ACL шары | проверить на реальной шаре в Срезе 3 |
|
||||
| 31 | 2026-10-04 | Задача 4.2: временный файл `.part` лежит прямо в `Input`; читается один `<job_id>.json` (старый код читал все файлы `Output/<id>/`) | спека; реальная ИС может забирать файлы без маски — риск вынесен в `open-questions-proposal.md` | запись `.part` в служебный каталог, чтение всех файлов подкаталога |
|
||||
| 32 | 2026-10-04 | R-10 (наблюдение O-1): оба реальных ответа `fixtures/ocr/aep_5.json` и `aep_8_form4.json` сами являются ответами-ошибками ИС (`error = INVALID_DOCUMENT`, пустой `data_ocr`), поэтому путь «успех -> `NOT_IMPLEMENTED`» проверяется на синтетическом успешном ответе (копия `aep_5.json` с `error = null` и одной строкой таблицы) — в unit-тестах 5.1 и интеграционном тесте 6.1 (временный каталог фикстур с `ok_doc.json`); реальные фикстуры используются для сценария «ошибка ИС проброшена» | невозможно получить успешный сценарий из имеющихся фикстур; спека не меняется | если владелец ждёт, что `aep_5.json` — успешный образец, нужны реальные успешные ответы (открытый вопрос Среза 3) |
|
||||
| 32 | 2026-10-04 | R-10 (наблюдение O-1): оба реальных ответа `fixtures/ocr/aep_5.json` и `aep_8_form4.json` сами являются ответами-ошибками ИС (`error = INVALID_DOCUMENT`; `data_ocr` — объект с пустыми полями (`form_name` пусто, `confidence 0.0`) и `table_structure: []`), поэтому путь «успех -> `NOT_IMPLEMENTED`» проверяется на синтетическом успешном ответе (копия `aep_5.json` с `error = null` и одной строкой таблицы) — в unit-тестах 5.1 и интеграционном тесте 6.1 (временный каталог фикстур с `ok_doc.json`); реальные фикстуры используются для сценария «ошибка ИС проброшена» | невозможно получить успешный сценарий из имеющихся фикстур; спека не меняется | если владелец ждёт, что `aep_5.json` — успешный образец, нужны реальные успешные ответы (открытый вопрос Среза 3) |
|
||||
| 33 | 2026-10-04 | R-11: эмулятор читает обычные переменные окружения через собственные настройки (`SCAN_HOTFOLDER_PATH`, `SCAN_INPUT_DIR`, `SCAN_OUTPUT_DIR`, `SCAN_FIXTURES_DIR`, `SCAN_EMULATOR_DELAY`, `SCAN_EMULATOR_POLL_SECONDS`), а не `Settings` шлюза; `process_once` синхронный, `delay` спится один раз за проход, ответ пишется атомарно (`.part` + rename), затем входящий файл переименовывается в `.done` | контейнеру эмулятора не нужны переменные MinIO/NATS | нет |
|
||||
| 34 | 2026-10-04 | Задача 4.3: без аргументов `from_env` подмешивает `.env`/`ENV_FILE` под `os.environ` (чтобы `make emulator` работал с `api/.env`); сообщение «нет пары» — `<имя без uuid-префикса, с расширением>: нет эталонного ответа в фикстурах`; битая фикстура -> ответ `INVALID_DOCUMENT` с сообщением о повреждённой фикстуре, вход получает `.done`; нет `Input/` — не ошибка, нет корня при старте — код выхода 2, нет каталога фикстур — warning | удобство dev; защита от зацикливания | нет |
|
||||
| 35 | 2026-10-04 | R-12: `Normalizer.normalize(raw: object)` получает уже разобранный JSON ответа ИС; `json.loads` и отображение ошибки разбора в `INVALID_FORMAT` — на стороне воркера (D-7); `NullNormalizer` возвращает `INVALID_FORMAT` для не-объекта JSON, пробрасывает код ошибки ИС, если он входит в `ErrorCode` контракта (регистр значим), иначе `UNKNOWN_ERROR` с исходным кодом в сообщении; пустой `message` = отсутствует (текст по умолчанию) | D-5 писал `raw: dict`, а «не JSON» из плана 5.1 нельзя разобрать, не имея байтов | если воркер захочет отдать декодирование нормализатору — перенести `json.loads` |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user