Диагностика симулятора¶
Справочник к разделу 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: queued → running → done | 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).