Перейти к содержанию

Диагностика симулятора

Справочник к разделу README.md: коды выхода, JSON-ошибки, типовые сбои стенда.

Коды выхода процесса sim

Реестр — SimExit в simulate/src/geoscan_sim/cli/exit_codes.py:10-14.

RC Имя Когда
0 OK Прогон завершён, preflight_codes пуст или только «мягкие» коды без violation после прогона (см. preflight_exit_code, simulate/src/geoscan_sim/cli/exit_codes.py:30-42)
1 FATAL План не принят до прогона (контракт, fatal preflight), или fatal после PAYLOAD_INCOMPATIBLE и т.п.
2 VIOLATIONS Preflight нашёл violation/warning и прогон всё же выполнен (политика should_run_with_preflight, exit_codes.py:45-51)
2 (validate) Эталонная таблица V-1…V-14 содержит FAIL (reference_exit_code)
3 INTERNAL Необработанное исключение в прогоне; в том числе повторный sim run в тот же --output (коллизия журнала мапится в INTERNAL_ERROR, не в RUN_ID_COLLISION — см. merge-log волны 2)

Проверка fatal preflight (пример с этой машины):

$ cd simulate && .venv/bin/sim run fixtures/mutants/m06_ir_on_gemini.json --output /tmp/sim-doc-m06
{"code": "PAYLOAD_INCOMPATIBLE", "message": "Нагрузка несовместима с бортом", "path": "", "severity": "fatal"}
$ echo $?
1

Структурированные ошибки

CLI и API отдают объекты с полями code, message, path, severity. Канонический реестр кодов — simulate/contracts/error_codes.yaml (классы fatal, violation, warning, …).

Полезные коды при эксплуатации:

Код Класс Что проверить
SCENARIO_MISSING fatal В JSON-плане есть scenario_id (см. fixtures/plans/s01.json)
PAYLOAD_INCOMPATIBLE fatal Пара uav_model / payload из docs/task5/10-hardware/fleet.yaml и payloads.yaml
WORLD_NOT_FOUND fatal (API) Имя мира из fixtures/worlds/ или алиас --world (runtime.py:16-18)
RUN_QUEUE_FULL — (HTTP 429) Очередь POST /api/v1/runs переполнена (run_store.py:125-127)
RUN_NOT_FOUND — (HTTP 404) Неверный run_id или прогон ещё не создан на диске
INTERNAL_ERROR fatal Лог stderr sim serve, место на диске, пустой каталог --output для повторного CLI-прогона

HTTP-стенд

Эндпоинт Назначение
GET /healthz Жив ли процесс (ops.py:12-14)
GET /readyz Готовность (ops.py:17-22)
GET /metrics Prometheus-текст (ops.py:25+); scrape-конфиг в репозитории нет — см. LIMITATIONS.md
GET /api/v1/runs/{id} status: queuedrunningdone | failed (run_store.py:145-206)

При --no-auth middleware предупреждает в лог, что /api/v1/* открыты (cli/__init__.py:207-210).

Вердикт приёмки плана

GET /api/v1/runs/{id}/verdict и sim verdict --run <id|path> --json делегируют в compute_verdict (api/verdict.py, verdict.py). Трёхзначный NO_GO / NO_FINDINGS при низкой полноте слоёв мира не выдаётся — типичный ответ UNRESOLVED с блокером LAYER_COMPLETENESS_LOW. Подкоманда sim verdict зарегистрирована (tests/defects/test_b6_stand.py::test_sim_verdict_subcommand_emits_tri_state).

Пример ответа (демо s01, как в 04-api/http-api.md):

{
  "verdict": "UNRESOLVED",
  "layer_completeness": { "obstacles": 0.0, "zones": 0.0 },
  "layer_completeness_score": 0.0,
  "blockers": [
    {
      "code": "LAYER_COMPLETENESS_LOW",
      "message": "Полнота слоя мира 0.0000; требуется 1.0 по всем слоям."
    }
  ]
}

Регрессия без сети

cd simulate
.venv/bin/pytest -q
.venv/bin/sim validate --reference   # RC 0, если все V-* PASS
.venv/bin/sim mutate --set base       # RC 0, если все мутанты пойманы

На tip ветки pytest -q может завершиться с RC≠0 из‑за известных падающих тестов (сессия 2026-09-17: 334 passed, 6 xfailed, 2 failed — test_http_manifest_race.py, test_zone_rules_b8.py); команда всё равно валидна как запуск регрессии, без обещания полностью зелёного прогона.

Полный офлайн-стенд docker compose up не собран — см. LIMITATIONS.md и xfail test_simulate_compose_file_exists (tests/defects/test_b6_stand.py:20-25).