Merge-лог: слияние принятых пакетов исправлений geoscan (Б-1…Б-9)¶
Ветка-приёмник — feat/geoscan-m1, база слияния — 5e046484
(«docs(task5): merge-log приёмки волны 4 geoscan»). Область — продукт geoscan,
всё вне simulate/. Дата слияния — 17.09.2026.
Блокеры и шаги воспроизведения взяты дословно из
git show accept/geoscan-final:docs/task5/96-acceptance/01-blockers.md.
Разборы ревью — /home/qovalenko/geoscan-queue/fix/reviews/review-geoscan-*.md.
1. Что влито¶
| Пакет | Блокер | Вердикт ревью | Влит | Чем подтверждено |
|---|---|---|---|---|
fix/gs-clean-clone |
Б-7 Из чистого клона проект не запускается | ПРИНЯТО | да, 84bdf0d7 |
Настоящий git clone в /tmp/gs-clean, без make: uv run pytest --collect-only → 275 tests collected; npm ci && npm run build в gateway/ → BUILD_EXIT=0 без ручной сборки shared |
fix/gs-energy-model |
Б-1 Валидатор не допускает ни один план | ПРИНЯТО | да, d015be2a |
s01/s02/s06 × 2 критерия: G3-04 ok, ВЕРДИКТ: план допущен, EXIT=0. По всем 10 сценариям × 2 критерия допущено 18 из 20 (на базе — 0) |
fix/gs-nfz-avoid |
Б-2 План заходит в НФЗ со статусом feasible | ПРИНЯТО | да, 78645f9b |
s02: violations.nfz 1610,07 → 0,0; валидатор G1-01…G1-04 ok, 0,000 m; числа планировщика и валидатора совпали |
fix/gs-mixed-fleet |
Б-3 Смешанный парк: пустой план feasible | ПРИНЯТО | да, eff73b7a (конфликт) |
s04: 0 миссий/coverage 0.0 → 1 миссия fw-201, coverage 0.3398, наружу UAV_UNUSED×2 + PARTIAL_COVERAGE |
fix/gs-objective-total |
Б-4 «Минимум налёта» проигрывает своей же функции | ПРИНЯТО | да, ba413af7 |
s03: total_flight_time_s в режиме total_flight_time 3452,84 → 2095,92 с, что теперь меньше 2300,92 с режима makespan (на базе было в 1,5 раза больше) |
fix/gs-export-sorties |
Б-9 GeoJSON не различает вылеты | ПРИНЯТО | да, a9d564cb |
s06-wind: у 36 маршрутных фич появились sortie (1/2), task_id, transect_id, start_s, duration_s; уникальных (uav_id, sortie, seq) — 36 из 36 (на базе 24 из 39, sortie отсутствовал) |
fix/gs-export-layers |
Б-8 REST-экспорт теряет слои сценария | ПРИНЯТО | да, 9936acd9 |
Живой прогон REST (gateway + planner gRPC, s02-nfz): <Placemark> REST=27 против CLI=27 (на базе 19 против 23), <Document>scenario в REST появился, слой nfz REST=3 (был 0) |
fix/gs-deploy-contract |
Б-6 Деплой в k3s сломан by-construction | ПРИНЯТО | да, ce205174 |
Живой python -m validator serve: /healthz → HTTP 200 {"status":"ok"}, /readyz → HTTP 200 {"ready":true}, /metrics отдаёт geoscan_validator_violations_total. На базе HTTP-сервера нет вовсе (HTTP 000). 70-migrate-job.yaml в kustomization.yaml, geoscan-validator в scrape-конфиге |
2. Что НЕ влито¶
| Пакет | Блокер | Вердикт ревью | Влит |
|---|---|---|---|
fix/gs-test-quality |
Б-5 Тестовый контур маскирует дефекты | НА ДОРАБОТКУ | нет |
Ветка fix/gs-test-quality @ 007527e9 остаётся в репозитории; в feat/geoscan-m1
не вливалась. Разбор — §6.
3. Конфликты и как они решены¶
Пакеты резались по блокерам, а не по файлам, поэтому пересечения были ожидаемы. Фактически пересеклись четыре файла.
3.1. src/backend/planner/planner/pipeline.py — единственный текстовый конфликт¶
Конфликт при слиянии fix/gs-mixed-fleet поверх уже влитого fix/gs-nfz-avoid,
в хвосте plan(). Обе стороны переписали одну и ту же строку
return draft.model_copy(update={"metrics": metrics}), каждая — своим сторожем статуса:
nfz-avoid(Б-2):violations.nfz/violations.airspace> 0,01 →INFEASIBLE;mixed-fleet(Б-3): пустой план при нулевом покрытии →INFEASIBLE, плюс предварительное добавление предупрежденияPARTIAL_COVERAGE.
Решение — оба, а не один. Это два независимых основания понизить статус,
и они не конкурируют: ни один из сторожей статус не повышает, поэтому порядок
между ними безразличен, а выбор «взять свою сторону» молча потерял бы один из
двух блокеров. Собранная версия: сначала PARTIAL_COVERAGE (он должен попасть
в warnings до проверки unassigned or warnings), затем оба сторожа, затем одно
обновление модели metrics + warnings + solver. Форма обновления взята из
mixed-fleet (updates: dict), потому что nfz-avoid не переносил
изменившийся список warnings в итоговый план — при его форме
PARTIAL_COVERAGE/UAV_UNUSED наружу бы не вышли.
Коммит eff73b7a, итоговый фрагмент — pipeline.py:823-846.
3.2. pipeline.py, ветка полос — пересечение без текстового конфликта¶
Git слил автоматически, но правки семантически пересекаются, поэтому проверено руками:
nfz-avoidзаменил источник полос:split_into_bands(poly, …)→ разбиение вырезанной областиctx.flyable_parts;mixed-fleetрасширил условие входа в эту ветку (use_area_bands = options.naive_equal_split or len(spacings) > 1) и добавил внутрь полосы вызовassign_transects.
После слияния живы обе правки: полосы строятся из flyable_parts, а вход в ветку
открыт и для смешанного парка. Проверено прогоном s02 (НФЗ, одна ширина полосы)
и s04 (без НФЗ, три ширины) — оба дают ожидаемые ревью числа.
3.3. planner/tests/test_pipeline_defects.py — противоречие двух тестов одного места¶
Слито автоматически, но результат разобран отдельно, потому что здесь два теста одного и того же утверждения противоречили друг другу:
test_d75_energy_frac_uses_reserve_budget(был на базе, сохранён в веткахnfz-avoidиmixed-fleetкак неизменённый хвост) требуетenergy_used_frac = used / budget_s, то есть от бюджета с вычетом резерва;test_b1_energy_used_frac_matches_validator_g3_04(пакетenergy-model) требует доли полного запаса хода и проверяет это через реальный вердикт валидатораG3-04.
Неверен был d75. Ту же семантику «доля полного запаса» независимо задают
контракт (90-business/08-data-model.md:325), фикстуры валидатора
(validator/tests/fixtures.py:260), фронтендовая модель
(src/frontend/packages/domain/src/planner.ts:583) и эталонные планы
(src/backend/plans/s01-simple.plan.json:47 = 0,7128, а не 0,89).
Git разрешил это верной стороной сам (удаление из energy-model победило,
так как соседние ветки строку не меняли); решение подтверждено и оставлено.
Сопутствующая правка при слиянии: три документа ссылались на удалённый тест и
описывали отменённую семантику. Актуализированы (коммит с merge-логом):
docs/task5/94-fixes/planner-core.md:25 (D-75 → «ОТМЕНЕНО (Б-1)»),
docs/task5/00-brief/04-requirements-checklist.md:56,121 (ссылки на test_b1_…).
3.4. test_solver_optimize.py, uv.lock, proto/geoscan/planner.proto¶
test_solver_optimize.py:nfz-avoidубралs02из списка скрытых исключений гейта,objective-totalдобавил проверку R-OPT-2. Правки в разных местах файла, слиты без конфликта, обе сохранились.uv.lock:clean-clone(build-зависимостьgeoscan-proto) иdeploy-contract(prometheus-clientу валидатора) — непересекающиеся записи, авто-слияние. После слиянияuv syncпересобралgeoscan-protoиgeoscan-validator.planner.proto:export-layersдобавилbytes scenario_json = 4в ответ. После слияния стабы перегенерированы (uv sync --reinstall-package geoscan-proto), TS-типы —npm run proto:genвнутриnpm run build.
3.5. Правка куратора вне пакетов: гонка в тесте очереди¶
tests/integration/test_gateway_data.py::test_queue_full_returns_429_with_retry_after
падал 2 прогона из 5 в одиночку (assert 202 == 429). Причина в тесте, не в продукте:
он сеял ровно PLAN_QUEUE_LIMIT = 2 задачи, пока gateway остановлен SIGSTOP, и после
SIGCONT воркер (PLAN_CONCURRENCY=1) успевал забрать одну в running раньше, чем
долетал POST /api/plans — очередь оказывалась короче предела.
Флейк предсуществующий: файл последний раз менялся волной 3
(70b7d7b3 gateway-data), ни один из влитых пакетов Б-1…Б-9 его не трогает.
Починено минимально (коммит 3f9880a7): сеем limit + 20 задач. Проверяемый инвариант
не ослаблен — тест по-прежнему требует 429, Retry-After: 30 и errorCode: QUEUE_FULL.
После правки 6 прогонов из 6 зелёные.
4. Тесты продукта после всех слияний¶
Ничего не исключалось, набор гонялся целиком.
4.1. Python: uv run pytest -q (из src/backend)¶
########## ФИНАЛЬНЫЙ ПРОГОН: uv run pytest -q
..........ssss.......................................................... [ 26%]
........................................................................ [ 52%]
........................................................................ [ 78%]
........................................................... [100%]
=============================== warnings summary ===============================
.venv/lib/python3.12/site-packages/_pytest/pathlib.py:95
/home/qovalenko/geoscan-wt/fix-curator-gs/src/backend/.venv/lib/python3.12/site-packages/_pytest/pathlib.py:95: PytestWarning: (rm_rf) error removing /tmp/pytest-of-qovalenko/garbage-8e749f96-c83f-4b78-be33-bdca7507b710
<class 'OSError'>: [Errno 39] Directory not empty: '/tmp/pytest-of-qovalenko/garbage-8e749f96-c83f-4b78-be33-bdca7507b710'
warnings.warn(
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
271 passed, 4 skipped, 1 warning in 127.55s (0:02:07)
Набор устойчив: три полных прогона подряд зелёные, в том числе один со случайным
порядком (pytest-randomly включён по умолчанию) и два с -p no:randomly;
последний — 271 passed, 4 skipped, 1 warning in 118.04s (0:01:58).
4 skipped — предсуществующий гейт tests/e2e по поднятому стенду
(tests/e2e/conftest.py), к слиянию отношения не имеет.
4.2. gateway: npm test (vitest)¶
✓ src/common/fastify/request-id.plugin.spec.ts (2 tests) 6ms
✓ src/config/config.schema.spec.ts (2 tests) 8ms
✓ src/config/env-example.sync.spec.ts (1 test) 6ms
✓ src/fleet/fleet.catalog.spec.ts (2 tests) 7ms
✓ src/plans/plans.repository.spec.ts (2 tests) 5ms
✓ src/plans/plans-export.service.spec.ts (1 test) 7ms
✓ src/openapi.snapshot.spec.ts (1 test) 138ms
Test Files 7 passed (7)
Tests 11 passed (11)
4.3. frontend: npm test (vitest)¶
✓ packages/domain/src/format.spec.ts (4 tests) 28ms
✓ packages/api-client/src/endpoints/scenarios.spec.ts (1 test) 11ms
✓ packages/api-client/src/client.spec.ts (3 tests) 19ms
✓ packages/state/src/plan-poll.spec.ts (4 tests) 20ms
✓ packages/state/src/mappers.spec.ts (4 tests) 19ms
✓ packages/domain/src/validation-temperature.spec.ts (1 test) 155ms
Test Files 11 passed (11)
Tests 24 passed (24)
4.4. Архитектурные контракты: uv run lint-imports --config .importlinter¶
Analyzed 116 files, 493 dependencies.
-------------------------------------
Валидатор не видит планировщик KEPT
Ядро без транспорта KEPT (2 ignored imports)
Контракты без геометрии KEPT
Слои валидатора KEPT
Contracts: 4 kept, 0 broken.
5. Невоспроизводимость блокеров на feat/geoscan-m1¶
Каждый блокер прогнан по шагам из 01-blockers.md дважды: на базе слияния
5e046484 (отдельный worktree /tmp/gs-merge-base, туда скопированы proto-стабы
и собраны node-зависимости) и на слитой ветке.
Б-1 — валидатор не допускает ни один план планировщика¶
Дословные команды блокера, s01/s02/s06 × 2 критерия:
база 5e046484 |
feat/geoscan-m1 |
|
|---|---|---|
| s01/s02/s06 | ВЕРДИКТ: план НЕ допущен, EXIT=4, FAIL G3-04 |
ok G3-04, ВЕРДИКТ: план допущен, EXIT=0 |
Расширенно, все 10 сценариев × 2 критерия (planner run → validator check):
s01-simple makespan/total EXIT=0 FAIL=[]
s02-nfz makespan/total EXIT=0 FAIL=[]
s03-multi-sites makespan/total EXIT=0 FAIL=[]
s04-mixed-fleet makespan/total EXIT=0 FAIL=[]
s05-mixed-survey makespan/total EXIT=0 FAIL=[]
s06-wind makespan/total EXIT=0 FAIL=[]
s07-endurance makespan/total EXIT=4 FAIL=[G3-01]
s08-infeasible-gsd makespan/total EXIT=0 FAIL=[]
s09-high-wind makespan/total EXIT=0 FAIL=[]
s10-replan makespan/total EXIT=0 FAIL=[]
18 планов из 20 допущены (на базе — 0 из 10). Блокер не воспроизводится. Остаток по s07 — §6.3, это не Б-1 и не регрессия слияния.
Б-2 — план заходит в НФЗ со статусом feasible¶
planner run --scenario scenarios/s02-nfz.json --objective makespan:
| база | ветка | |
|---|---|---|
solver.status |
feasible |
feasible |
violations.nfz |
1610,074 | 0,0 |
валидатор G1-01 |
FAIL 971,096 m |
ok 0,000 m |
валидатор G1-04 (буфер) |
FAIL |
ok 0,000 m |
| расхождение планировщик/валидатор | 1610,07 против 971,10 | 0,0 против 0,000 — сошлись |
coverage_frac |
0,9999 | 0,9846 |
Падение покрытия на 1,5 % — физически непокрываемый коридор буфера 25 м вдоль зоны,
цена обхода; makespan вырос 1021,4 → 1107,9 с. Блокер не воспроизводится.
Б-3 — смешанный парк: пустой план со статусом feasible¶
planner run --scenario scenarios/s04-mixed-fleet.json --objective makespan:
| база | ветка | |
|---|---|---|
missions |
0, [] |
1, ['fw-201'] (Геоскан 201, как и требует expected.note) |
coverage_frac |
0,0 | 0,3398 |
unassigned |
147 × endurance_exceeded |
123 × endurance_exceeded |
warnings |
[] — наружу ничего |
['UAV_UNUSED', 'UAV_UNUSED', 'PARTIAL_COVERAGE'] |
Формулировка блокера («0 миссий, покрытие 0.000, ошибки наружу нет») не
воспроизводится. Оговорка ревью сохраняется: статус остался feasible при 34 %
покрытия, наружу это выходит предупреждением, а не статусом — §6.2.
Б-4 — «минимум суммарного налёта» проигрывает по своей же функции¶
planner run --scenario scenarios/s03-multi-sites.json с двумя критериями:
база: makespan |
база: total_flight_time |
ветка: makespan |
ветка: total_flight_time |
|
|---|---|---|---|---|
total_flight_time_s |
2300,98 | 3452,84 | 2300,92 | 2095,92 |
total_distance_m |
20 309,8 | 32 728,4 | 20 309,2 | 19 159,2 |
| миссий | 3 | 2 | 3 | 2 |
На базе режим total_flight_time давал налёт в 1,5 раза больше, чем makespan.
На ветке он даёт 2095,92 < 2300,92 с, то есть выигрывает по собственной целевой
функции; дистанция тоже стала меньше. Блокер не воспроизводится.
Б-6 — деплой в k3s сломан by-construction¶
Прогон на живом кластере не выполнялся (как и в самой приёмке — вывод блокера статический). Проверено кодом и манифестами, плюс живой запуск сервиса локально:
| база | ветка | |
|---|---|---|
python -m validator serve, GET :OPS_HTTP_PORT/healthz |
HTTP 000 — HTTP-сервера нет |
HTTP 200 {"status": "ok"} |
/readyz |
— | HTTP 200 {"ready": true} |
/metrics → geoscan_validator_violations_total |
метрики нет в коде | # HELP geoscan_validator_violations_total … |
70-migrate-job.yaml в kustomization.yaml |
нет (0 вхождений) | есть |
geoscan-validator в scrape-конфиге Prometheus |
нет (0 вхождений) | targets: ["validator.geoscan.svc.cluster.local:3002"] |
Пробы k3s/geoscan/40-deployment-validator.yaml:52-62 (/readyz, /healthz на :3002)
теперь имеют за собой реальный сервер. Блокер не воспроизводится.
Живой apply -k на n2 остаётся невыполненным — §6.4.
Б-7 — из чистого клона проект не запускается¶
Настоящий git clone в /tmp/gs-clean, make на машине отсутствует (NO MAKE),
стабы в клоне не лежат (в proto-gen/src/geoscan/ только __init__.py),
shared/dist отсутствует.
| Шаг блокера | база | ветка |
|---|---|---|
uv run pytest -q |
6 errors during collection, ImportError: cannot import name 'validator_pb2_grpc' |
275 tests collected in 2.08s — стабы генерирует uv sync через proto-gen/setup.py |
npm run build в gateway/ без ручной сборки shared |
26 ошибок TS (в чистом клоне вовсе exit 127: нет node_modules) |
BUILD_EXIT=0; prebuild сам делает npm --prefix ../shared ci && run build |
Блокер не воспроизводится. Побочно это же объясняет 13 ERROR в
tests/integration, которые видели все ревьюеры: у них не был собран
shared/dist, и модульные фикстуры gateway падали на npm run build.
После clean-clone эти 13 ошибок исчезли.
Б-8 — REST-экспорт теряет слои сценария, включая НФЗ¶
Живой прогон REST-пути (planner gRPC + node dist/main.js, POST /api/plans
на s02-nfz, затем GET /api/plans/:id/export/kml), сравнение с
python -m planner export на том же плане:
| база | ветка | |
|---|---|---|
<Placemark> REST против CLI |
19 против 23 | 27 против 27 |
<Document>scenario в REST-KML |
нет | есть |
вхождений слоя nfz в REST-KML |
0 (в CLI — 3) | 3 (в CLI — 3) |
вхождений allowed (разрешённое ВП) |
0 (в CLI — 1) | 1 (в CLI — 1) |
| фич в REST-GeoJSON | 19 | 27 |
Оператор, открывший KML из веб-интерфейса, снова видит бесполётные зоны. Блокер не воспроизводится.
Б-9 — GeoJSON-экспорт не различает вылеты¶
planner export --fmt geojson на плане s06-wind (2 вылета):
| база | ветка | |
|---|---|---|
| фич всего | 39 | 39 |
фич со свойством sortie |
0 | 36 (номера вылетов 1, 2) |
transect_id |
0 | 36 |
task_id |
1 | 37 |
start_s / duration_s (время, Q-08) |
0 / 0 | 36 / 36 |
уникальных (uav_id, sortie, seq) |
24 из 39 | 37 из 39 |
Оставшиеся 2 неуникальные тройки — это 3 слоя сценария (у них sortie: null);
у всех 36 маршрутных фич тройка уникальна. R-OUT-8 в GeoJSON восстановим.
Блокер не воспроизводится.
6. Что осталось незакрытым¶
6.1. Б-5 — пакет fix/gs-test-quality не принят, вход в следующую итерацию¶
Вердикт ревью (заход 3 из 3): НА ДОРАБОТКУ. Перечисленная в Б-5 маскировка
снята по-настоящему (скрытый deselect s02/s07/s10, CI фронтенда, barriers.sh,
спека-копия request-id, тавтологии, забор rel=0.5), но пакет внёс собственные
дефекты. Задание следующего захода, дословно по разбору:
docs/task5/10-hardware/fleet.yaml— убратьgeoscan_gemini.altitude_agl_min: 100. Константа подогнана под ожидаемое число теста и противоречит первоисточнику (assets/pdf/geoscan-gemini-specs.pdfминимума не содержит) и четырём производным документам, где минимум 100 м AGL закреплён только за Геоскан 201. Справочник ТТХ под ожидаемый результат теста не правится.test_ensure_scenario_acceptable_rejects_unreachable_gsd_on_s08_fleet— перестроить так, чтобы он не опирался на выдуманную константу.planner/rpc/convert.py— убрать фильтр по ветру из проверки достижимости GSD. Пакет сломал RPC-путь на s09: сценарий, дающий план на базе, начинал падать сInfeasible: GSD_UNREACHABLE, вопреки BR-23 иexpectedсамого s09.- Добавить тест на п. 3 (
run_plan_streamна s09 не бросаетInfeasible). - Гейт
report.admitted is Trueразвести поexpected.input_error— сейчас параметр s08 не может позеленеть никогда, это такой же тупик, как скрытый skip. scripts/barriers.sh— вернуть gateway-typecheck иnpm testв исполняемую часть: приset -euo pipefailкрасный python-прогон обрывал скрипт и выбивал node-барьеры из job'а.docs/task5/98-fixes/test-quality.md:12— убрать неверное утверждение про ветер на RPC-пути.
Следствие для продукта — дыры в тестовом контуре остаются открытыми:
planner/tests/test_solver_optimize.py:97-100— скрытый deselect s02/s07/s10 внутри теста «validator zero violations» всё ещё на месте для s07 и s10 (s02 снялnfz-avoid). Гейт по-прежнему не краснеет от s07/s10;- CI фронтенда (
.gitea/workflows/frontend-quality.yaml) гоняет только typecheck+build: 11 spec-файлов иtools/verify-numbers.tsв CI не исполняются (локально зелёные — §4.3, но CI их не защищает); scripts/barriers.shгоняет толькоtests/barriers(10 тестов из 275):planner/tests,validator/tests,tests/integrationне запускаются ни одним job'ом. То есть зелёный CI на этой ветке не равен зелёному §4.1;- тавтологии и вакуумные циклы в
test_pipeline_defects.py, заборrel=0.5вvalidator/tests/test_checks.py:94, спека-копияgateway/src/common/fastify/request-id.plugin.spec.ts— в силе.
Это самый тяжёлый незакрытый долг: §4 доказывает работоспособность этим прогоном, а не автоматикой, которая поймает регрессию завтра.
6.2. Хвосты принятых пакетов (ревью приняли с оговорками)¶
- Б-3, остаток. Полосы делятся поровну по площади, без учёта возможностей борта: 32-минутный мультиротор получает ту же треть 357-километрового полигона, что и Геоскан 201, не отлетает её, и полоса теряется. Отсюда покрытие 0,34 на s04 и s10. Смешанный парк формально перестал быть пустым, но смешанным ещё не стал.
- Б-3, статус. Пустой план стал
infeasible, но план с покрытием 0,34 остаётсяfeasible— наружу это выходит только предупреждениемPARTIAL_COVERAGE. - Б-3, данные.
s04-mixed-fleetвname/descriptionзаявляет «~40 км²», фактическая площадь ~357 км². Правка данных сценария вынесена за пакет. - Б-2, бюджет возврата.
pipeline.py:231всё ещё оценивает возврат домой по прямой, тогда как фактический возврат строится обходом. На текущих сценариях не стреляет; на сценарии, где обход длиннее хорды, дастG3-01 FAIL. - Б-2, полосы по нескольким частям.
split_into_bandsвызывается для каждой части клипа отдельно и результаты складываются в один список, а выбор идёт поbands[i]— при клипе из более чем одной части полосы со второй части молча потеряются. Проверено: на всех 10 сценариях клип даёт ровно 1 часть, поэтому сейчас не стреляет. Латентный дефект, унаследованный отnfz-avoid. - Б-2, покрытие на s02.
G6-01 warn 0,985 < 0,99— непокрытый коридор буфера 25 м. Физически ожидаемо, но BR-21 для s02 декларирует покрытие 1,0; формулировку BR-21 стоит согласовать с наличием буфера. - Б-4, слабая модель стоимости.
mip_assign_transectsостался с упрощённой стоимостью и теперь обслуживает толькоpareto/naive— то естьparetoунаследовал ровно ту модель, из-за которой ломалсяtotal_flight_time. - Б-6, фикстура.
tests/integration/test_validator_grpc_server.py:69-73—ops_http_portне запрашиваетgrpc_server_port; тест проходит благодаря порядку в файле, в одиночку упадёт. Под-k/xdist набор станет красным. - Б-6, дубль.
validator/validator/rpc/ops.py— построчная копияplanner/planner/rpc/ops.py; кандидат на вынос вshared-py. - Б-7, quickstart.
docs/task5/95-manual/02-quickstart.md:52-66по-прежнему предписывает ручнойgrpc_tools.protoc. Шаг стал избыточным, но для жюри выглядит как обязательная ручная работа. Обновлён толькоsrc/backend/README.md. - Б-7, мёртвый код.
proto-gen/generate_stubs.py:36—stubs_present()нигде не используется. - Б-7, побочные эффекты теста.
tests/test_clean_clone_b7.pyправит рабочее дерево (SHARED_DIST.unlink()) и делаетnpm ciпри обычномuv run pytest— сетевой и разрушающий шаг внутри python-прогона. - Б-8, покрытие теста. Новый gRPC-тест гоняется на
s01-simple, где НФЗ нет;tests/e2e/test_stand_acceptance.pyне утверждает наличие<Document>scenario. - Б-9, накопление времени.
export/geojson.py:60-61—continueнаlen(ph.path) < 2стоит до накопленияphase_start_s; при пустой геометрии этапа время последующих этапов «уедет». Сейчас таких этапов нет ни на одном сценарии.
6.3. Дефекты вне области Б-1…Б-9, не закрытые никем¶
G3-01 на s07-endurance — единственный оставшийся недопуск.
Перерасход бюджета вылета: gemini-01/s17: 1921 с при бюджете 1920 с,
gemini-01/s21: 1931 с при бюджете 1920 с.
Это не регрессия слияния — проверено прямым прогоном на базе 5e046484:
# база 5e046484
FAIL G3-01 energy Бюджет вылета с резервом 170.753 s
FAIL G3-04 energy Согласованность energy_used_frac 42.000 шт
ВЕРДИКТ: план НЕ допущен
EXIT=4
# feat/geoscan-m1 после слияния
FAIL G3-01 energy Бюджет вылета с резервом 170.722 s
ВЕРДИКТ: план НЕ допущен
EXIT=4
Величина практически не изменилась (170,753 → 170,722 с), G3-04 ушёл.
Ни один из пакетов Б-1…Б-9 эту проверку не закрывает: Б-1 назвал ровно одну
причину — G3-04. Похоже на ошибку округления при дроблении галса
(split_transect). Завести отдельной задачей.
Побочно слиянием закрылись G6-03 («нет ни одного этапа turn») на s04/s09/s10
и G3-01 на s10, которые ревью Б-1 фиксировало как ничейные.
6.4. Регрессии слияния¶
Не обнаружено. Все три класса проверок (§4 тесты, §5 воспроизведение блокеров,
контракты import-linter) зелёные; ни один влитый пакет не отменил правку соседа.
Единственное падение, встреченное при слиянии, —
test_queue_full_returns_429_with_retry_after, — предсуществующий флейк самого теста,
разобран и починен в §3.5.
Отдельно зафиксирован наблюдавшийся один раз флейк
tests/integration/test_gateway_data.py::test_readyz_503_when_postgres_down
(URLError на /readyz после docker stop контейнера Postgres; в одиночку и в
последующих полных прогонах — зелёный). Тест того же семейства «останови инфраструктуру
и успей опросить за 5 с»; к влитым пакетам отношения не имеет, но кандидат на
ужесточение таймингов в пакете Б-5.
6.5. Организационное — до раскатки¶
- Образы стенда протухли.
docker-compose.ymlиsrc/backend/docker-compose.ymlпиннятBUILD_COMMIT: 7173c9af,k3s/geoscan/*— тот же тег. После этого слияния образы надо перевыпустить с новым sha, иначе и compose-стенд, и k3s поедут на сборке без Б-1…Б-9. Тег образа — 8-символьный sha этого merge-коммита. apply -kна n2 не выполнялся. Проверка Б-6 статическая плюс локальный запуск сервиса; живой прогонkubectl --context n2 apply -k k3s/geoscan/и выход пода validator вReady— за раскаткой.ttlSecondsAfterFinished: 86400при фиксированном имени Job по sha: повторныйapply -kтого же образа спустя сутки пересоздаст Job и прогонит миграции заново — требует идемпотентности мигратора, нигде не зафиксированной.