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

00 — Сводный чеклист ревью реализации

Ревью ветки feat/implementation-review (8 коммитов реализации, f420004..422e43a) против 90-business/, 91-system/, 92-code/ и первоисточников 00-brief/, 30-domain/, 40-formats/, 50-stack/. Дата: 2026-09-16. Метод: 10 параллельных ревьюеров-специалистов (каждый статус подтверждён файл:строка или прогоном) + агент-критик: перепроверены все 77 строк «ГОТОВО» (3 понижены до «ЧАСТИЧНО», пометка «Критик:» в доказательстве) и 15 выборочных «НЕТ» (ложных не найдено). Подробности — в отчётах 01-…10-, дефекты — в 99-defects.md, как читать — в README.md.

Сводка

Уровень ГОТОВО ЧАСТИЧНО НЕТ ВНЕ ОБЪЁМА НЕ ПРОВЕРЕНО Всего
Бизнес-требования (90-business + чеклист ТЗ) 28 136 40 2 1 207
Системные требования (91-system) 8 60 107 3 2 180
Архитектурные принципы A-01…A-15 (50-stack/05) 2 10 2 1 0 15
Кодовая база и аспекты реализации (92-code и пр.) 36 18 41 2 12 109
Итого 74 224 190 8 15 511

Бизнес-требования (90-business + чеклист ТЗ)

ID Требование Статус Доказательство Что доделать Отчёт
BR-06 Температура вне диапазона — борт не летит НЕТ grep -rn temperature validator/ пусто; frontend packages/domain/src/validation.ts:311-318 — статус 'pass' зашит Реализовать отсечение по temperature_range из fleet.yaml 01-business-trace.md
BR-07 Площадка пригодна для способа взлёта/посадки (suitable_for) НЕТ grep -rn suitable_for src/backend/validator src/backend/planner — только тест validator/tests/test_contracts.py:26 Добавить проверку и учёт при назначении площадки 01-business-trace.md
BR-20 Точка посадки вне запретов с учётом эллипса сноса НЕТ grep -rni "эллипс|drift" src/ — вхождений нет Реализовать проверку области вероятного приземления 01-business-trace.md
BR-28 Разведение бортов (Δh ≥ 50 м либо горизонтальный интервал) НЕТ grep -rn "separation|разведен" src/backend пусто; во фронтенде только группа validation.ts:78 id:'separation' Реализовать отключаемую проверку в валидаторе 01-business-trace.md
FR-EXP-09 KML под импорт в Geoscan Planner: стили, отраслевые имена элементов НЕТ export/kml.py:34-64; на выгрузке s01 t.count("<Style")=0, t.count("extrude")=0, имена Placemark — takeoff 0 / transit 1 / survey 1 вместо «Галс 1 (ПАФС)» (40-formats/01:155) (также в 01-business-trace.md: ЧАСТИЧНО) Стили survey/transit + styleUrl, extrude, русские отраслевые имена, description с метриками 04-planner-core.md
FR-EXP-10 Вспомогательные слои: область съёмки, NFZ, граница ВП, ВПП, полосы захвата НЕТ export/geojson.py:8-41 принимает scenario и не использует его ни разу; в выгрузке только LineString этапов (также в 01-business-trace.md: ЧАСТИЧНО) Добавить слои из сценария и полосы захвата отдельными FeatureCollection/Folder 04-planner-core.md
FR-EXP-15/16 Выгрузка .plan (QGC) и .waypoints НЕТ ExportFormat содержит PLAN и WAYPOINTS, export/init.py:19 бросает NotImplementedError В FR помечены «дополнительно» — допустимо отложить, но убрать значения из enum либо документировать как нереализованные 04-planner-core.md
FR-EXP-16 Выгрузка .waypoints (QGC WPL 110) НЕТ grep -rn "waypoints|WPL" src/ — вхождений нет Реализовать текстовый экспорт 01-business-trace.md
FR-EXP-18 Генерализация геометрии при импорте НЕТ apps/planner/src/screens/ExportScreen.tsx:179 — только текст про Дугласа-Пекера; реализации нет Реализовать либо убрать утверждение из интерфейса 01-business-trace.md
FR-MET-07 Профиль ветра по высоте НЕТ scenario.py:126-131 — только скалярный ветер; потребителей профиля нет Расширить схему и расчёт 01-business-trace.md
FR-MET-08 Температура сценария и предупреждение о выходе за диапазон НЕТ Wind.temperature_c есть (scenario.py:130), потребителей нет; см. BR-06 Добавить предупреждение 01-business-trace.md
FR-MON-01 Приём состояния выполнения плана НЕТ packages/domain/src/store.ts:360-363 — только droppedUavIds; полей выполненных галсов/позиций/остатка заряда нет ни в сторе, ни в geoscan_contracts Ввести модель состояния выполнения 01-business-trace.md
FR-MON-03 Бюджет борта в воздухе = остаток заряда НЕТ packages/domain/src/planner.ts:186-190 — борт исключается, остальные считаются с полным endurance; battery_left_frac (scenario.py:55) нигде не читается Учесть остаток заряда при пересчёте 01-business-trace.md
FR-PLN-03 Вычитание бесполётных зон и пересечение с границей разрешённого ВП, буфер безопасности НЕТ grep -rn "nfz|no_fly|airspace" src/backend/planner/ → только pipeline.py:506-507 (нули в метриках); scenario.airspace в pipeline.plan() не читается. Прогон s02: transects crossing NFZ: 3 of 8, ~810 м внутри зоны, при этом violations.nfz=0 (также в 01-business-trace.md: ЧАСТИЧНО) Реализовать coverage/airspace_clip.py: вычитание NFZ с буфером objective.safety_buffer_m, пересечение с airspace.allowed, учёт floor/ceiling и высотных границ зон 04-planner-core.md
FR-PLN-04 Декомпозиция невыпуклой области с дырами НЕТ coverage/transects.py:53 — используется только poly.coordinates[0]; baseline/equal_split.py:51-52 «convex MVP» (также в 04-planner-core.md: ЧАСТИЧНО) Трапецоидальная/бустрофедонная декомпозиция с учётом дыр 01-business-trace.md
FR-PLN-05 Непокрытый остаток показан числом, покрытие не маскируется НЕТ pipeline.py:476,497 — planned считается из тех же work[...]["transects"] (pipeline.py:434-436), coverage_fraction тождественно 1.0. Прогон s02: reported coverage_fraction 1.0, честная цель «площадь минус NFZ» = 0.8636 Считать покрытие как площадь объединения полос захвата ∩ полигон / площадь полигона (30-domain/03:212); swath_polygon уже есть и нигде не используется 04-planner-core.md
FR-PLN-10 Разрезание галса на подгалсы под бюджет вылета НЕТ planner/planner/pipeline.py:174 — вылет закрывается только при непустом taken; одиночный длинный галс берётся целиком Резать галс; не влезающий подгалс — в unassigned с причиной 01-business-trace.md
FR-PLN-12 Между вылетами одного борта заложен оборот (замена АКБ / заряд) НЕТ pipeline.py:255 clock += used. Полигон 25 км²: sortie 2 start_min 18.57 при dur_min 18.57 первого, 41 вылет подряд без зарядки; turnaround_min грузится (catalog.py:327) и не используется Ограничение C8 (30-domain/04:35, 05:158): turnaround_time_min при свободной АКБ, иначе charge_time_min; учесть spare_batteries площадки 04-planner-core.md
FR-PLN-13 Маршрутизация с несколькими ВПП, посадка на другую площадку НЕТ planner/planner/pipeline.py:241-251 — launch_site=home.id, landing_site=home.id; прогон дал [('h','h'),('h','h')] Реализовать VRP с выбором площадок старта/посадки, поддержать landing_sites 01-business-trace.md
FR-PLN-23 Бюджет времени решателя и статус остановки НЕТ pipeline.py:489 — time_limit_s только копируется в solver; таймера и прерывания в коде нет Ввести таймер, лучшее найденное решение и stop_reason 01-business-trace.md
FR-PLN-24 Неназначенные галсы списком с машиночитаемой причиной НЕТ pipeline.py:305-311 конструирует Unassigned по именам полей, schema.py:236-242 без populate_by_name. Прогон «geoscan_201 + riebo_r4»: RAISED ValidationError: 4 validation errors for Unassigned (transectId Field required / transect_id Extra inputs are not permitted) Починить конструирование, ввести коды причин (UnassignedReason), покрыть тестом; учитывать галсы, выброшенные упаковкой 04-planner-core.md
FR-RPT-08 График масштабируемости НЕТ ls src/backend/scenarios — только s01-simple.json, s02-nfz.json, s03-multi-sites.json; сценариев sc-fleet/sc-area/sc-sites/sc-nfz нет Завести сценарии масштабируемости и собрать график 01-business-trace.md
FR-SCN-04, FR-EXP-17 Импорт KML/KMZ/GeoJSON/SHP с пересчётом CRS НЕТ apps/shell/src/screens/StartScreen.tsx:151 — false}> без обработчика; src/frontend/README.md «разбор файла — нет»; в бэкенде парсеров нет Реализовать разбор, определение CRS и назначение ролей слоям 01-business-trace.md
FR-SCN-06 ЛАФС: осевая линия и ширина коридора НЕТ grep -rni "коридор|corridor|лафс" src/ — единственное вхождение packages/domain/src/scenarios.ts:84 (имя БПЗ) Добавить тип задания «коридор» и генерацию геометрии 01-business-trace.md
FR-STE-08 Курс захода и взлёта против ветра НЕТ grep -rni "заход|approach" packages/domain/src — логики нет; pipeline.py:229-239 — этап landing в точке старта Строить маршрут посадки как функцию ветра 01-business-trace.md
FR-TSK-08 Окно времени задания учитывается при планировании НЕТ Поле есть в shared-py/geoscan_contracts/scenario.py:100, но grep -rn time_window src/backend/planner src/backend/validator — ни одного использования Учесть окно в расписании и в проверке валидатора 01-business-trace.md
FR-TSK-09 Слияние RGB и мультиспектра одним проходом на Pollux НЕТ grep -rn rgb_capable src/ — вхождений нет Реализовать слияние проходов 01-business-trace.md
NFR-02, NFR-03 Диапазон масштабных прогонов и график solve_time_s НЕТ Те же три сценария; колонка solve_time в validator/report/table.py есть, данных нет Завести наборы sc-* и собрать таблицу 01-business-trace.md
NFR-09 Контур поднимается одной командой НЕТ find . -iname 'docker-compose*' — ничего; Dockerfile только у gateway и packages/api-client Добавить docker-compose.yml и воспроизводимую инструкцию 01-business-trace.md
NFR-12, NFR-13 Локальные тайлы/DEM и завендоренные шрифты НЕТ src/frontend/README.md «Известные ограничения»: шрифты из системного стека, подложка — один фоновый слой; find src/frontend -name '.woff2' -o -name '.pmtiles' — пусто Завендорить шрифты, собрать офлайн-тайлы 01-business-trace.md
NFR-13 Шрифты интерфейса вендорены локально (woff2) НЕТ find src/frontend -name '.woff2' -o -name '.woff' -o -name '*.ttf' (без node_modules) → пусто; packages/tokens/src/global.css:29-30 задаёт 'Inter'/'JetBrains Mono' с системным фолбэком; src/frontend/README.md:160 сам фиксирует это как ограничение Завендорить woff2 и @font-face в packages/tokens 10-build-ops.md
NFR-19 Перезапуск не теряет сценарии и планы НЕТ gateway/src/app.module.ts:9-15 — модуля БД нет; plans.controller.ts:31-36 возвращает случайный UUID и «очередь — в GW-07» Реализовать реестр планов 01-business-trace.md
NFR-21 Идемпотентность повторного расчёта НЕТ Idempotency-Key отправляется клиентом (packages/api-client/src/endpoints/plans.ts:101-105), на сервере не читается (grep -rn Idempotency src/backend пусто) Реализовать на гейтвее 01-business-trace.md
NFR-22 Недоступность доменного процесса ≠ пустой план НЕТ validate.controller.ts:19-30 возвращает status:'stub'; маппинг UNAVAILABLE→503 есть только как хелпер shared/src/grpc/grpc-to-http.ts без потребителя Подключить gRPC-клиентов и обработку недоступности 01-business-trace.md
NFR-24 Сквозной идентификатор запроса в журнале НЕТ common/interceptors/request-id.interceptor.ts есть, но структурированного журнала нет (grep -rn "pino|logger" gateway/src пусто) Ввести журналирование с request_id 01-business-trace.md
NFR-26 Доступ к доменным процессам закрыт сервисным ключом НЕТ SERVICE_API_KEY только читается (config/config.service.ts:12-14); guard отсутствует (grep -rn "CanActivate|Guard" gateway/src пусто) Добавить проверку ключа на gRPC-канале 01-business-trace.md
NFR-28 Безопасный импорт файлов (25 МБ, XXE, понятные ошибки) НЕТ Разбора файлов нет вовсе (см. FR-SCN-04); BODY_LIMIT_BYTES=26214400 в gateway/.env.example закрывает только размер тела Реализовать вместе с импортом 01-business-trace.md
NFR-34 Шесть обязательных разделов документации отдельными файлами НЕТ find docs -iname 'user' -o -iname 'ограничени' — только 90-business/03-user-scenarios.md; поставочного комплекта нет Собрать поставочный комплект документации 01-business-trace.md
R-DOC-5 Руководство пользователя НЕТ grep -rln "руководство пользователя" docs — только упоминания в ТЗ и требованиях; документа нет Написать 01-business-trace.md
R-DOC-6 Перечень известных ограничений НЕТ Сводного перечня нет: ограничения рассыпаны по src/frontend/README.md и docs/task5/TODO.md Свести в один документ 01-business-trace.md
BR-01 Допустимость плана = семь жёстких счётчиков равны нулю ЧАСТИЧНО Валидатор: geoscan_contracts/plan.py:42-62 (семь видов), admission.py:47-55, тест test_clean_plan_is_admitted. Планировщик пишет пять захардкоженных нулей (pipeline.py:505-511, подтверждено прогоном) Убрать самопровозглашённые нули из плана планировщика 01-business-trace.md
BR-03 W > wind_max → борт не летит, строка «доступно N из M» ЧАСТИЧНО Отсечение pipeline.py:304, проверка checks/g4_weather.py:27-72; строки счётчика нет (grep «доступно» по src/frontend пусто) Показать счётчик доступных бортов 01-business-trace.md
BR-04 |W⊥| < V_a на каждом галсе, V_g > 0 ЧАСТИЧНО Валидатор: checks/g4_weather.py:75-170 (тесты test_crosswind_above_airspeed_is_a_violation, test_crosswind_gain_matches_theory). Планировщик бросает Infeasible на весь прогон (geometry/wind.py:20-21) Не назначать конкретный галс вместо падения всего расчёта 01-business-trace.md
BR-05 Совместимость борт ↔ нагрузка ↔ тип съёмки ЧАСТИЧНО Валидатор checks/g5_assignment.py:20-47; каталог compatibility (тест test_catalog_compatibility_matrix). Планировщик сверяет только payload.survey_type == task.survey_type (pipeline.py:302), матрицу модель↔нагрузка не проверяет Сверять физическую совместимость модели с нагрузкой 01-business-trace.md
BR-08, BR-09 Бюджет endurance·(1−reserve) со всеми фазами ЧАСТИЧНО Резерв pipeline.py:123; валидатор checks/g3_energy.py:20-61 (тест test_endurance_budget_is_checked_with_reserve) и структурная проверка g3_energy.py:64-98. Маршрут посадки из трёх точек не строится Добавить маршрут посадки в бюджет 01-business-trace.md
BR-11, BR-12, BR-13 Высоты: 100 м AGL у 201, не ниже старта, потолки 500/4000 м ЧАСТИЧНО Валидатор checks/g2_altitude.py:24-118 (тест test_altitude_above_ceiling_is_a_violation); планировщик geometry/optics.py:57-74. «Не ниже точки старта» сведено к altitude_m < 0; недостижимая высота роняет весь прогон, а не исключает борт Исключать борт-кандидата; ввести превышение площадки 01-business-trace.md
BR-14 Выше 150 м — предупреждение, не блокировка ЧАСТИЧНО Валидатор g1_airspace.py:184-217; планировщик предупреждение не эмитирует (warnings пуст в прогоне) Эмитировать предупреждение в плане 01-business-trace.md
BR-16 Огибание рельефа, GSD постоянен ЧАСТИЧНО Высоты AGL (geoscan_contracts/plan.py:112,122), проверка соответствия высоты GSD (checks/g2_altitude.py:121-162). Модели рельефа нет, и это честно объявлено: checks/g2_altitude.py:19-21 MISSING_CHECKS G2-04 Ввести DEM и проверку разброса GSD 01-business-trace.md
BR-17, BR-18, BR-19, BR-21 Ноль в БПЗ и вне ВП, буфер, покрытие = область минус БПЗ ЧАСТИЧНО Валидатор закрывает все четыре: checks/g1_airspace.py:26-181, metrics.py:38-71 (покрытие по площади, тест test_lying_coverage_metric_fails). Планировщик — ни одного (зон не читает; coverage_fraction = доля числа галсов, pipeline.py:497) Реализовать в планировщике 01-business-trace.md
BR-22, BR-23 Недостижимый тип съёмки или GSD → ошибка валидации входа ЧАСТИЧНО См. FR-TSK-07, FR-VAL-01, FR-VAL-02: сейчас unassigned или Infeasible по ходу расчёта, без перечня возможностей парка Оформить как ошибку входа с матрицей возможностей 01-business-trace.md
BR-25 Галсы поперёк ветра, проигрыш иного угла показан ЧАСТИЧНО Автовыбор pipeline.py:69-96; теория подтверждена тестом validator/tests/test_optics.py::test_crosswind_gain_matches_theory. Показа величины проигрыша нет Показать разницу времени против автоугла 01-business-trace.md
BR-26 Маршрут посадки обязателен и строится против ветра ЧАСТИЧНО Этап landing есть (pipeline.py:229-238), но это точка старта с нулевой геометрией; зависимости от ветра нет Построить заход из трёх точек против ветра 01-business-trace.md
BR-27 При выбытии борта работа сохраняется ЧАСТИЧНО Только фронтенд-переброс парка (store.ts:360-363); выполненные галсы не моделируются, инварианта сохранения работы нет См. FR-MON-01…FR-MON-03 01-business-trace.md
FR-ASP-01, FR-ASP-02 Inclusion разрешённого ВП и exclusion БПЗ на всех этапах ЧАСТИЧНО Валидатор: checks/g1_airspace.py:26-102, тесты test_no_fly_zone_intrusion_is_measured_in_meters и test_outside_allowed_airspace_is_measured. Планировщик зоны и границу игнорирует (grep "no_fly|allowed" pipeline.py пусто) Учесть при построении покрытия и перелётов 01-business-trace.md
FR-ASP-03 Рисование зон и импорт, блокировка незамкнутого контура ЧАСТИЧНО store.ts:177 (draw-nfz), блокировка <3 вершин — ScenarioScreen.tsx:193, AirspaceScreen.tsx:94; импорт зон отсутствует Реализовать импорт зон 01-business-trace.md
FR-ASP-04 Объёмная зона: ограничение по высотному слою ЧАСТИЧНО Валидатор закрывает: validator/checks/g1_airspace.py:44-49,105-132 + context.covers_altitude; планировщик зоны не читает вовсе (grep no_fly в pipeline.py пусто) Учесть зоны и их высотный слой при построении покрытия 01-business-trace.md
FR-ASP-05 Тип зоны и срок действия ЧАСТИЧНО Контракт Zone.zone_kind/valid_from/valid_to (scenario.py:103-114); валидатор учитывает срок (validator/context.py:197-199); планировщик и фронтенд — нет Учесть в построении и показать в интерфейсе 01-business-trace.md
FR-ASP-06 Буфер безопасности от границ зон, дефолт 25 м ЧАСТИЧНО validator/checks/g1_airspace.py:135-181 (эквидистанта); objective.safety_buffer_m в planner/ не используется (grep пусто) Применять буфер при генерации галсов 01-business-trace.md
FR-ASP-07 Предупреждение «выше 150 м — разрешение на ИВП» ЧАСТИЧНО Валидатор: checks/g1_airspace.py:184-217 (тест test_ivp_threshold_is_warning_not_violation). Планировщик считает above_ivp_threshold=True (pipeline.py:469), но warnings пуст (прогон) Эмитировать ALT_ABOVE_150M в план 01-business-trace.md
FR-ASP-08 Таблица вершин ВП с азимутами и расстояниями, правка ЧАСТИЧНО apps/planner/src/screens/AirspaceScreen.tsx:15,46 — таблица с bearingDeg/distanceM; правки вершины из таблицы не найдено Добавить редактирование из таблицы 01-business-trace.md
FR-ASP-09 Непротиворечивость входа по воздушному пространству ЧАСТИЧНО Предвалидации нет ни в планировщике, ни в валидаторе; во фронтенде только постфактум-группа validation.ts:205 id:'airspace' Добавить предвалидацию входа 01-business-trace.md
FR-EXP-01 Индивидуальное ПЗ на каждый борт ЧАСТИЧНО Фильтр по борту есть (export/kml.py:9-11, export/geojson.py:9), но без --uav KML содержит несколько в одном файле с именем первого борта (kml.py:22); пакетной выгрузки нет (также в 04-planner-core.md: ГОТОВО) Сделать выгрузку набором файлов по числу бортов 01-business-trace.md
FR-EXP-02 Выгрузка полётного задания в KML ЧАСТИЧНО export/kml.py:16-53: namespace kml/2.2, Document/Folder/Placemark, relativeToGround в каждом LineString, координаты lon,lat,alt с 7 знаками (выгрузка s01 проверена). Нет XSD-проверки; takeoff/landing выгружаются вырожденным LineString из двух совпадающих точек вместо Point (40-formats/01:127-130) (также в 01-business-trace.md: ГОТОВО) XSD-валидация в CI, Point для взлёта/посадки, стили 04-planner-core.md
FR-EXP-03 Выгрузка полётного задания в GeoJSON ЧАСТИЧНО export/geojson.py:16-35: RFC 7946 FeatureCollection, порядок [lon, lat], altitude_frame "AGL". Но высота не третьим элементом позиции (40-formats/01:15); метаданные коллекции — только {"scenario_id": …} вместо uav_model/payload/survey_type/gsd_cm/total_time_s/wind/generated_at (40-formats/01:26-42) 3D-координаты и метаданные уровня FeatureCollection 04-planner-core.md
FR-EXP-03, FR-EXP-11 GeoJSON по RFC 7946 и форматные проверки ЧАСТИЧНО export/geojson.py:26-29 — координаты 2D lon/lat; properties на уровне FeatureCollection (geojson.py:31-35) вне RFC 7946, высота выброшена, точность не нормирована (в KML нормирована — export/common.py:13-14) Привести метаданные к RFC, добавить высоту и нормировать точность 01-business-trace.md
FR-EXP-04 Маршрут и ключевые точки с номером и типом ЧАСТИЧНО geoscan_contracts/plan.py:83-101 (Phase.seq, Phase.phase); экспорт сохраняет порядок (export/kml.py:38-54); round-trip-тест проходит. Критик: seq не инкрементируется между turn/transit и survey (pipeline.py:180,194) — пара этапов делит один номер, в KML «transit 1»/«survey 1»; round-trip не ловит (tests/integration/conftest.py:21-45, рукописные seq) Инкрементировать seq на каждом этапе; round-trip на реальном плане планировщика 01-business-trace.md
FR-EXP-04/06/08 Порядковый номер, тип этапа, скорость, длительность в выгрузке ЧАСТИЧНО В GeoJSON у Feature только uav_id, phase, altitude_m, altitude_frame; нет seq, duration_s, speed_ms, transect_id, photos, trigger_interval. В KML атрибутов нет. Плюс seq дублируется: phase_seq не инкрементируется после transit/turn (pipeline.py:179-190) — в KML видно takeoff 0 / transit 1 / survey 1 Перенести атрибуты этапа в properties/ExtendedData, починить нумерацию seq 04-planner-core.md
FR-EXP-05 Высота и явный фрейм высоты во всех форматах одинаково ЧАСТИЧНО KML: altitudeMode (export/kml.py:50, тест planner/tests/test_kml_export.py). GeoJSON: altitude_frame только в properties, высот в координатах нет (geojson.py:22-29) (также в 04-planner-core.md: ГОТОВО) Добавить высоту третьей координатой в GeoJSON 01-business-trace.md
FR-EXP-06 Скорость в полётном задании ЧАСТИЧНО Фронтенд пишет speed_ms (packages/domain/src/exporters.ts:72,166); бэкенд-экспорт скорость не пишет (export/kml.py:41-53, export/geojson.py:18-24) Добавить скорость в бэкенд-экспорт 01-business-trace.md
FR-EXP-07 Место старта и посадки в ПЗ ЧАСТИЧНО В плане поля есть (schema.py:196-197), в выгрузку не попадают — площадки не экспортируются (export/kml.py, export/geojson.py) Добавить точки старта и посадки в выгрузку 01-business-trace.md
FR-EXP-08 Этап как явный атрибут каждого сегмента ЧАСТИЧНО GeoJSON: properties.phase (geojson.py:21); KML: этап только в (kml.py:47), без ExtendedData Добавить ExtendedData в KML 01-business-trace.md
FR-EXP-12 Round-trip экспорт → импорт сохраняет геометрию и атрибуты ЧАСТИЧНО tests/integration/test_kml_roundtrip.py и test_geojson_roundtrip.py проходят, но сверяют только съёмочные этапы синтетического плана (tests/integration/conftest.py:21-90); своего разбора нет — парсинг внутри теста Реализовать импорт и расширить round-trip на скорости, этапы, площадки 01-business-trace.md
FR-EXP-13, R-DEMO-5 Экспорт KML/GeoJSON из веб-интерфейса ЧАСТИЧНО apps/planner/src/screens/ExportScreen.tsx + packages/domain/src/exporters.ts:236 downloadText — файлы формируются в браузере из клиентского плана, не из плана сервиса Подключить выгрузку через API 01-business-trace.md
FR-EXP-14 Программная выгрузка по идентификатору плана ЧАСТИЧНО Контракт есть: proto/geoscan/planner.proto:7,19-28 (ExportPlan); HTTP-маршрута и реализации нет (gateway/src/plans/plans.controller.ts — заглушки) Реализовать маршрут и серверную часть 01-business-trace.md
FR-EXP-15 Выгрузка .plan QGroundControl с geofence и rally points ЧАСТИЧНО Фронтенд: packages/domain/src/exporters.ts:174 missionToQgcPlan (geofence/rally не найдены). Бэкенд: export/init.py:19 — NotImplementedError Реализовать в бэкенде, добавить geofence и rally points 01-business-trace.md
FR-FLT-01, FR-FLT-09 Парк из трёх моделей ТЗ; reference_only не предлагать ЧАСТИЧНО Каталог из docs/task5/10-hardware/fleet.yaml (catalog.py, gateway/src/fleet/fleet.catalog.ts), GET /api/fleet; grep -rn reference_only src/ — вхождений нет Отфильтровать справочные модели 01-business-trace.md
FR-FLT-02, FR-FLT-04 Борт-экземпляр с установленной нагрузкой; совместимость по установленной ЧАСТИЧНО Контракт Uav с единственным payload (scenario.py:46-55); валидатор checks/g5_assignment.py:20-47 (тест test_incompatible_payload_is_a_violation). Планировщик использует свою FleetUnit (schema.py:62-70), available_from игнорируется Свести схемы, учесть время готовности 01-business-trace.md
FR-FLT-05 Включение/выключение борта, блокировка при пустом парке ЧАСТИЧНО pipeline.py:299-303 фильтрует по u.enabled; явной блокировки с сообщением нет — план просто пустой Добавить явную ошибку входа 01-business-trace.md
FR-FLT-06 Лётные параметры борта как ограничения входа ЧАСТИЧНО Скорость/запас/ветер/потолки читаются (schema.py:19-33, pipeline.py:123,158,304); дальность не учитывается (grep по range в src/backend пусто) Добавить ограничение по дальности 01-business-trace.md
FR-FLT-07 Минимальная безопасная высота и «не ниже точки старта» ЧАСТИЧНО Валидатор: checks/g2_altitude.py:56-118. Планировщик: optics.py:57-74 проверяет altitude_agl_min, а can_fly_below_launch_point сведено к altitude_m < 0 (optics.py:73-74) Ввести рельеф/превышение площадки и сравнение с высотой старта 01-business-trace.md
FR-FLT-08 Запас хода по времени и видимый резерв 20 % ЧАСТИЧНО Резерв применён (pipeline.py:123), дефолт 0.20 (schema.py:121, scenario.py:139); энергетической модели по Вт·ч нет Добавить интерфейс энергетической модели 01-business-trace.md
FR-MET-03 Отсечение по wind_max и строка «доступно N из M» ЧАСТИЧНО Отсечение pipeline.py:304; валидатор checks/g4_weather.py:27-72 (тест test_wind_above_uav_limit_is_a_violation). grep -rn "доступно" src/frontend — вхождений нет Показать счётчик доступных бортов 01-business-trace.md
FR-MET-05 Автовыбор угла галсов и ручная фиксация ЧАСТИЧНО Автовыбор pipeline.py:69-96; ручная фиксация task.angle_deg (pipeline.py:339-343); разница по времени против автоугла не показывается Показывать величину проигрыша (см. FR-PLN-07) 01-business-trace.md
FR-MET-06 Правка ветра с пересчётом плана ЧАСТИЧНО Фронтенд пересчитывает локально (store.ts:331-358); серверного пересчёта нет — гейтвей заглушка Подключить пересчёт через API 01-business-trace.md
FR-MON-02 Пересчёт на остатке невыполненных галсов ЧАСТИЧНО store.ts:360-363 replanWithout — борт добавляется в droppedUavIds и план считается заново целиком; выполненные галсы не фиксируются Реализовать пересчёт на остатке с инвариантом сохранения работы 01-business-trace.md
FR-MON-04 Новый план проходит полный набор жёстких проверок ЧАСТИЧНО store.ts:345 вызывает клиентский validate() на 24 проверки, а не бэкенд-валидатор Валидировать пересчёт на сервере 01-business-trace.md
FR-MON-05 Сопоставление нового плана с исходным ЧАСТИЧНО Сравнение планов есть (apps/validator/src/screens/ParetoScreen.tsx — diff План A/План B); доли выполненных работ и остатка нет Показать дельты и распределение переданных галсов 01-business-trace.md
FR-MON-06 Пересчитанный план выгружается теми же средствами ЧАСТИЧНО packages/domain/src/exporters.ts не различает исходный и пересчитанный план; ограничение наследуется от FR-MON-02 Закрыть вместе с FR-MON-02 01-business-trace.md
FR-PLN-02 Свой слой покрытия на каждое задание ЧАСТИЧНО Высота/шаг/перекрытия считаются на задание и на каждый борт (pipeline.py:315-330), но в TaskGeometry уходит только ctx.units[0] (pipeline.py:429-433) — при разнотипном парке отображаемые высота и шаг не соответствуют части галсов задания Отдавать слой на пару «задание × борт» либо диапазон значений 04-planner-core.md
FR-PLN-05, FR-RPT-07 Непокрытый остаток отдельным числом ЧАСТИЧНО Валидатор считает покрытие по площади (validator/metrics.py:38-71), но площадь выреза отдельно не выводит (report/json_report.py:29-46); планировщик coverage_fraction = доля числа галсов (pipeline.py:497) Выводить вырезанную и неназначенную площадь 01-business-trace.md
FR-PLN-06 Автоподбор угла галсов перебором θ=0…179 по «время галсов + развороты» ЧАСТИЧНО Перебор есть (pipeline.py:69-96), но в transect_time_s передаётся угол семейства, а не курс галса (transects.py:90) — разница 90°. Квадрат 1×1 км, ветер 8 м/с, V_a=10: chosen angle 3.0 → real_total 3935.5 s; angle 93.0 → 2419.6 s (в 1,63 раза быстрее), что совпадает с «+67 %» из 30-domain/03:95 (также в 01-business-trace.md: ГОТОВО) Передавать фактический track_deg галса; добавить тест s06 «автоугол не хуже фиксированного» 04-planner-core.md
FR-PLN-07 Ручной угол и показ разницы во времени против автоугла ЧАСТИЧНО Ручной угол работает (pipeline.py:339-343); сравнения времени нет ни в плане, ни в отчёте Добавить обе величины makespan в отчёт 01-business-trace.md
FR-PLN-08 Время галса по путевой скорости с учётом ветра; время плана включает развороты, перелёты, оборот ЧАСТИЧНО Формула V_g = sqrt(V_a²−W⊥²)+W∥ верна (geometry/wind.py:8-22). Но wind.py:33-34 усредняет полгалса туда/полгалса обратно, а pipeline.py:156-165 снова подставляет угол вместо курса: при ветре 8 м/с суммарное время съёмки в плане 2301.7 s против честных 3823.4 s (занижение в 1,66 раза); набора высоты и оборота нет Считать по фактическому курсу каждого галса, добавить T_climb и оборот на площадке 04-planner-core.md
FR-PLN-08, BR-24, NFR-33 Время по путевой скорости с разворотами, набором высоты и оборотом ЧАСТИЧНО Путевая скорость geometry/wind.py:8-34; валидатор ловит наивную формулу checks/g6_coverage.py:85-113 (тест test_plan_without_turns_fails_turn_time_check). В плане turns_total=0 и turn_time_fraction=0.0 при наличии этапов turn (прогон); оборот и набор высоты не моделируются Считать метрики разворотов, добавить оборот и набор высоты 01-business-trace.md
FR-PLN-09 Разворот как отдельный этап, два режима ЧАСТИЧНО Стоимость по режиму pipeline.py:61-66 (lzp/иначе), этап turn создаётся pipeline.py:178-188; геометрия разворота — прямая линия, метрики нулевые Дать этапу собственную геометрию и метрики 01-business-trace.md
FR-PLN-11 Бюджет вылета с перелётами, маршрутом посадки и резервом ЧАСТИЧНО Перелёт и возврат учтены (pipeline.py:157-174,215-228), резерв применён (pipeline.py:123); маршрута посадки из трёх точек нет (pipeline.py:229-238) Добавить маршрут посадки (BR-09, BR-26) 01-business-trace.md
FR-PLN-12, BR-10 Оборот между вылетами одного борта ЧАСТИЧНО Фронтенд: packages/domain/src/planner.ts:478,586. Бэкенд: pipeline.py:254 clock += used — вылеты встык. Валидатор проверку имеет (checks/g3_energy.py:101-145) Реализовать оборот в бэкенд-планировщике 01-business-trace.md
FR-PLN-14 Галс назначается только борту с совместимой нагрузкой ЧАСТИЧНО Фильтр по survey_type нагрузки: pipeline.py:299-303. Матрица compatibility «борт ↔ нагрузка» (payloads.yaml:206-224, Catalog.model_covers) не проверяется; suitable_for площадки (C7) в pipeline не читается ни разу (также в 01-business-trace.md: ГОТОВО) Проверять compatibility и пригодность площадки для типа борта 04-planner-core.md
FR-PLN-15 Борт с превышенным ветром исключается целиком и счётчик доступных ЧАСТИЧНО Исключение pipeline.py:304; счётчика нет (см. FR-MET-03) Показать счётчик доступных бортов 01-business-trace.md
FR-PLN-16 Высоты в пределах ТТХ борта (мин. безопасная, не ниже старта, потолки AGL/ASL) ЧАСТИЧНО Все четыре проверки в geometry/optics.py:57-74, тест test_altitude_infeasible_above_max проходит. Но исключение внутри цикла по бортам (pipeline.py:321) убивает весь расчёт: парк «Gemini + 801», GSD 10 см → RAISED Infeasible: ALTITUDE_ABOVE_MAX: altitude 510.6 m > max AGL 500.0 m, хотя 801 выполняет задание на 282 м (также в 01-business-trace.md: ГОТОВО) Исключать непригодный борт с причиной, падать только если не осталось ни одного 04-planner-core.md
FR-PLN-17 Критерий «минимум времени выполнения работ» (makespan) ЧАСТИЧНО Критерий доезжает до плана (pipeline.py:350,487) и меняет показатель степени весов (pipeline.py:350-358), но при однородном парке веса одинаковы: s03 даёт makespan_min 17.09 / flight_min 47.08 одинаково для makespan, total_flight_time и naive_equal_split Настоящая целевая функция (VRP/локальный поиск) либо честная формулировка «критерий влияет только на веса полос» 04-planner-core.md
FR-PLN-17, FR-PLN-18, R-OPT-1, R-OPT-2 Критерии «минимум makespan» и «минимум суммарного налёта» ЧАСТИЧНО Обе метрики считаются (pipeline.py:473-474; validator/metrics.py:74-85). Критерий влияет только на показатель степени весов: pipeline.py:350-358 (exponent=2.0 для total_flight_time) — оптимизации нет Реализовать решатель и сравнительный прогон по обоим критериям 01-business-trace.md
FR-PLN-18 Критерий «минимум суммарного налёта» ЧАСТИЧНО Тот же прогон s03: результат с Criterion.TOTAL_FLIGHT_TIME побитово совпадает с makespan-прогоном (flight_min 47.08) То же, что FR-PLN-17 04-planner-core.md
FR-PLN-19, R-OPT-3, R-DEMO-4 Выбор критерия в интерфейсе, отражение в плане и выгрузке ЧАСТИЧНО store.ts:322-329 (setCriterion/setLambda); solver.criterion заполняется (pipeline.py:487). В выгрузке критерия нет: export/geojson.py:31-35 — только scenario_id Положить критерий в метаданные выгрузки 01-business-trace.md
FR-PLN-20 Фронт Парето по λ ЧАСТИЧНО packages/domain/src/planner.ts:662 paretoFront(scenario, steps = 7) — 7 точек вместо 11; экран apps/validator/src/screens/ParetoScreen.tsx. В бэкенде λ влияет лишь на exponent (pipeline.py:356-358) Довести до 11 точек и реального решателя 01-business-trace.md
FR-PLN-21 Наивный бейзлайн и выигрыш в процентах ЧАСТИЧНО planner/planner/cli/main.py:46-56 (baseline), baseline/equal_split.py; строка выигрыша validator/report/table.py:117-136. Сквозной прогон невозможен (см. дефект D-1) Починить сквозной путь и зафиксировать числа 01-business-trace.md
FR-PLN-22, NFR-15 Детерминизм при фиксированном seed ЧАСТИЧНО Источников случайности в pipeline.py нет; аргумент rng объявлен и не используется (pipeline.py:276), seed только копируется (pipeline.py:490). Теста воспроизводимости среди 150 тестов нет Добавить регрессию «два прогона дают идентичные метрики» 01-business-trace.md
FR-PLN-24, BR-02 Неназначенные галсы с машиночитаемой причиной ЧАСТИЧНО Валидатор проверяет (checks/g5_assignment.py:96-153, тест test_unassigned_without_reason_fails). Планировщик пишет одну запись на задание с transect_id=-1 и свободной строкой (pipeline.py:305-313) Формировать unassigned по каждому галсу с кодом UnassignedReason 01-business-trace.md
FR-RPT-01 Сводка метрик целиком и в разрезе бортов и вылетов ЧАСТИЧНО validator/metrics.py:152-166 — все метрики целиком; разреза по бортам и вылетам в отчёте нет Добавить разрез по бортам и вылетам 01-business-trace.md
FR-RPT-02 Диаграмма Ганта по бортам, длина шкалы = makespan ЧАСТИЧНО apps/planner/src/components/GanttTimeline.tsx существует; данные — клиентский план, обороты в бэкенд-плане отсутствуют Подключить к серверному плану 01-business-trace.md
FR-RPT-03 Сравнение двух планов бок о бок с дельтами ЧАСТИЧНО apps/validator/src/screens/ParetoScreen.tsx (diff План A/План B); в бэкенде сравнения нет Добавить сравнение в отчёт валидатора 01-business-trace.md
FR-RPT-04 Отчёт валидатора как документ допуска ЧАСТИЧНО report/json_report.py:29-46 — scenario_id, criterion, validator_version, solve_time_s, вердикт, нарушения, предупреждения. Нет идентификатора плана, seed, бюджета времени, коммита, отметки времени Дописать недостающие поля 01-business-trace.md
FR-RPT-05 Машиночитаемая сводка и таблица для презентации ЧАСТИЧНО JSON: cli/run.py:47 и cli/check.py:20 (--json); текстовая таблица render_table. Выгрузки таблицы в файл нет Добавить экспорт таблицы 01-business-trace.md
FR-RPT-06, NFR-06 Одна команда на весь набор сценариев по обоим критериям ЧАСТИЧНО uv run python -m validator run --scenarios scenarios/ отработала, критерии по умолчанию makespan,total_time (cli/run.py:44); вывод — «нет плана» по всем трём сценариям, сценариев три из десяти Починить генерацию планов и довести набор до s01–s10 01-business-trace.md
FR-SCN-01 Сценарий как единый объект заданной схемы ЧАСТИЧНО Контракт полон: shared-py/geoscan_contracts/scenario.py:151-167, schema/scenario.schema.json; uv run python -m validator run --scenarios scenarios/ читает файлы без преобразований. Но у планировщика вторая схема geoscan_contracts/schema.py:133-148, у фронтенда — третья (packages/domain/src/types.ts) Свести к одной схеме, schema.py убрать или сделать адаптером 01-business-trace.md
FR-SCN-02 Три способа создать сценарий ЧАСТИЧНО apps/shell/src/screens/StartScreen.tsx:131-155 — карточки «Мастер», «Импорт», таблица демо; импорт файл не разбирает Реализовать импорт и пошаговый мастер 01-business-trace.md
FR-SCN-05 Невыпуклые полигоны, мультиполигоны, дыры ЧАСТИЧНО Контракт поддерживает (geoscan_contracts/geometry.py, тест tests/integration/test_geometry_contract_roundtrip.py проходит); планировщик — нет (см. FR-PLN-04) Поддержать в построении покрытия 01-business-trace.md
FR-SCN-08 Демо-сценарии s01–s10, статус «не загружен» ЧАСТИЧНО src/backend/scenarios — три файла; во фронтенде карточки s01–s10, полные данные только s04 (src/frontend/README.md) Довести набор до s01–s10 в машинной схеме 01-business-trace.md
FR-SCN-09 Валидация геометрии входа до расчёта ЧАСТИЧНО planner/planner/geometry/shapes.py:22-31 — make_valid/explain_validity, но не как входной контроль; диапазон координат и вырожденная площадь не проверяются Добавить предвалидацию геометрии с явными кодами ошибок 01-business-trace.md
FR-STE-01, FR-STE-02 Произвольные ВПП и несколько ВПП с привязкой борта ЧАСТИЧНО LaunchSite.position произвольный (scenario.py:23-34); s03-multi-sites.json c двумя ВПП (тест test_s03_has_two_launch_sites_and_three_uavs). Планировщик стартует с u.home, выбора площадки нет Реализовать выбор площадки (см. FR-PLN-13) 01-business-trace.md
FR-STE-03 Атрибуты ВПП: превышение, пригодность, курс взлёта ЧАСТИЧНО Поля есть (scenario.py:27-30), потребителей нет (см. BR-07) Применять при назначении бортов площадкам 01-business-trace.md
FR-STE-04 Резервные площадки посадки, старт ≠ финиш ЧАСТИЧНО LandingSite в контракте (scenario.py:37-43) и в s01..s03; в плане всегда landing_site == launch_site (прогон) См. FR-PLN-13 01-business-trace.md
FR-STE-05, FR-STE-09 Оборот на площадке, запасные АКБ, зарядные посты ЧАСТИЧНО Контракт turnaround_time_min/spare_batteries (scenario.py:32-34); валидатор checks/g3_energy.py:101-145. Планировщик оборот не закладывает (pipeline.py:254 clock += used); числа зарядных постов в схеме нет Реализовать оборот в планировщике, добавить число постов 01-business-trace.md
FR-STE-06 Правка/удаление площадок и блокировка без пригодной площадки ЧАСТИЧНО Инструмент place-site (packages/domain/src/store.ts:29); блокировки «нет пригодной площадки» нет Добавить блокировку (зависит от BR-07) 01-business-trace.md
FR-STE-07 Площадки внутри разрешённого ВП и вне БПЗ ЧАСТИЧНО Точки площадок входят в построение проекции (validator/context.py:168), но отдельной проверки среди 24 нет (checks/init.py:28-35) Добавить проверку 01-business-trace.md
FR-TSK-01 Задание = область + тип + качество + перекрытия + окно ЧАСТИЧНО Контракт полон (scenario.py:89-100); time_window не используется; планировщик читает задание из своей схемы schema.py:73-83 (нет quality) Свести схемы, учесть окно 01-business-trace.md
FR-TSK-02, FR-TSK-03 Пять типов съёмки и качество по типу ЧАСТИЧНО geoscan_contracts/enums.py — пять SurveyType; Quality с тремя взаимоисключающими метриками (scenario.py:58-79, тест test_quality_requires_exactly_one_metric). Планировщик принимает только gsd_cm (schema.py:79); LiDAR-шаг не выводится (geometry/optics.py:94-97) Довести LiDAR/геофизику до явной ошибки входа 01-business-trace.md
FR-TSK-04 Перекрытия по умолчанию из payloads.yaml ЧАСТИЧНО catalog.py overlap_defaults + тест test_catalog_overlap_defaults_are_fractions; планировщик жёстко 70/60 (schema.py:80-81) Брать дефолты из каталога по типу съёмки 01-business-trace.md
FR-TSK-06, FR-PLN-02 Несколько заданий, свой слой покрытия с собственным шагом ЧАСТИЧНО planner/planner/pipeline.py:298-348 — цикл по scenario.tasks со своей высотой/шагом/углом; pipeline.py:444-471 — свой TaskGeometry на задание. Критик: work[u.id] сохраняет altitude_m/spacing_m только первого задания (pipeline.py:386-402); прогон: галсы t2 нарезаны под 306 м, летятся на 153 м → продольное перекрытие 20 % вместо 60 % Хранить высоту/шаг по каждому заданию и прокидывать в survey-этапы 01-business-trace.md
FR-TSK-07 Недостижимое качество / нет нагрузки → ошибка валидации входа ЧАСТИЧНО Недостижимая высота → Infeasible (geometry/optics.py:57-74, тест test_altitude_infeasible_above_max); отсутствие совместимого борта даёт запись в unassigned (pipeline.py:305-313), без перечня возможностей парка Разделить предвалидацию и планирование, добавить матрицу возможностей в текст ошибки 01-business-trace.md
FR-VAL-01 Вход проверяется до запуска решателя ЧАСТИЧНО Infeasible бросается по ходу планирования (geometry/optics.py:57-74), CLI возвращает код 3 (cli/main.py:35-37); отдельного этапа предвалидации нет Ввести предвалидацию входа с объяснением возможностей парка 01-business-trace.md
FR-VAL-02 LiDAR/геофизика → явная ошибка с перечнем типов ЧАСТИЧНО Каталог знает пробелы (catalog.py, тесты test_catalog_compatibility_matrix, test_lidar_spacing_is_not_guessed); планировщик даёт unassigned, а не ошибку (pipeline.py:305-313) Сделать ошибку входа с матрицей возможностей 01-business-trace.md
FR-VAL-12 Недопущенный план выгружается только с пометкой ЧАСТИЧНО Вердикт допуска есть: validator/admission.py:47-55; в export/kml.py и export/geojson.py признака недопущенного плана нет (также в 05-validator.md: ГОТОВО) Пробросить признак в файлы и интерфейс 01-business-trace.md
FR-VAL-13 Прогон s01–s10 одной командой, все поля отчёта заполнены ЧАСТИЧНО ls src/backend/scenarios → s01-simple.json, s02-nfz.json, s03-multi-sites.json (3 из 10); план-фикстура только для s01 (validator/tests/fixtures.py:105 build_s01_plan); uv run python -m validator run --scenarios scenarios --criteria makespan,total_time → все три строки «нет плана» (также в 01-business-trace.md: ГОТОВО) Добавить сценарии s04–s10 и планы/бейзлайн под них 05-validator.md
FR-VAL-15 Разделы отчёта со счётчиками проверок ЧАСТИЧНО Шесть групп с фиксированным порядком: checks/base.py:28-45, рендер report/table.py (тест test_check_renders_human_table). Пять разделов ТЗ не совпадают с шестью группами; агрегатов по блокировкам и предупреждениям в JSON нет (report/json_report.py:29-46) Добавить агрегаты по группам 01-business-trace.md
FR-VAL-16 Машинно-применимое исправление у предупреждения ЧАСТИЧНО Только фронтенд на моках: packages/domain/src/validation.ts:13 (suggestedFix), apps/validator/src/screens/ReportScreen.tsx:23. В CheckResult бэкенда поля исправления нет (checks/base.py:48-60) Добавить поле исправления в контракт отчёта 01-business-trace.md
FR-VAL-17 Валидатор доступен наружу как операция над внешним планом (POST /validate) ЧАСТИЧНО CLI над чужим планом работает (uv run python -m validator check --scenario … --plan …), но gateway/src/validate/validate.controller.ts:24 возвращает {status:'stub'}; в validator/validator/ нет модуля rpc/; proto/geoscan/validator.proto:9-15 знает только bytes plan_json → repeated string violations (также в 01-business-trace.md: ГОТОВО) Поднять gRPC-сервер валидатора, расширить proto до полного отчёта, подключить GW-12 05-validator.md
FR-VAL-18 Регрессионный набор против отката к наивным формулам ЧАСТИЧНО Есть: test_projected_area_matches_geodesic, test_optics.py (GSD по таблице), проверка G6-02 строгого T>L/V_g. Нет: центроид выгрузки в bbox входа, makespan не растёт с числом бортов, воспроизводимость по seed Дописать три регрессии 01-business-trace.md
NFR-01 Расчёт демо-сценария за 10–15 с ЧАСТИЧНО Прогон планировщика на синтетическом полигоне 1,3 км²: solve_time 0.06 с. Демо-сценария 20–40 км² в репозитории нет, стенда нет Померить на демо-сценарии на стенде 01-business-trace.md
NFR-04 Бюджет времени и пометка «остановлено по лимиту» ЧАСТИЧНО Поле stop_reason в контракте есть (geoscan_contracts/plan.py:39), заполнять некому (см. FR-PLN-23) Реализовать вместе с бюджетом решателя 01-business-trace.md
NFR-05 Интерфейс не блокируется на время расчёта ЧАСТИЧНО store.ts:334 — setTimeout(...,30), расчёт в главном потоке; src/frontend/README.md: «Расчёт синхронный в главном потоке… на 200 км² заметно подвиснет» Вынести на бэкенд или в Web Worker, добавить прогресс и отмену 01-business-trace.md
NFR-07, NFR-08, R-PLT-1, R-PLT-2 Веб-сервис в браузере ПК, единственная публичная точка входа ЧАСТИЧНО src/frontend/apps/*/vite.config.ts (Vite + Module Federation), gateway/src/main.ts:45 — единственный HTTP-процесс. Прогоном не проверить: Node/npm нет, docker compose отсутствует Собрать контур и проверить на стенде 01-business-trace.md
NFR-11 Ни одного обращения к внешним ресурсам ЧАСТИЧНО src/frontend/README.md «Карта не ходит в сеть», при этом шрифты системные и офлайн-тайлы не собраны (NFR-12/13). Проверка сетевых запросов без сборки невозможна Проверить после вендоринга шрифтов и тайлов 01-business-trace.md
NFR-14 Экспорт KML/GeoJSON и отчёта в офлайн-контуре ЧАСТИЧНО Экспорт формируется без сети и в браузере (exporters.ts:236), и в бэкенде (planner/planner/export/*.py); офлайн-прогон не выполнялся Проверить на офлайн-стенде 01-business-trace.md
NFR-16 Сценарий несёт id, seed, expected; отчёт указывает коммит ЧАСТИЧНО Сценарий: scenario_id, solver.seed, expected (scenario.py:152-167, есть во всех трёх файлах scenarios/). В отчёте только validator_version (json_report.py:32); коммит отдаёт только gateway /status (shared/src/ops-http-contract.ts:10-24) Добавить seed и коммит в отчёт валидатора 01-business-trace.md
NFR-17 Числа на экране совпадают с отчётом валидатора ЧАСТИЧНО Фронтенд считает метрики сам: store.ts:331-358 вызывает solve/validate/paretoFront в браузере — ровно то, что NFR запрещает Перейти на серверные числа 01-business-trace.md
NFR-20 Честное сообщение о невыполнимости ЧАСТИЧНО Коды выхода CLI (validator/cli/exit_codes.py, planner/cli/main.py:33-40), исключения Infeasible/InvalidScenario (geoscan_contracts/exceptions.py); пользовательских объяснений «что парк умеет» нет Дописать тексты и матрицу возможностей 01-business-trace.md
NFR-23 Операционный контракт каждого процесса и метрики Prometheus ЧАСТИЧНО gateway/src/ops/ops.controller.ts:18-56 — четыре маршрута; /metrics содержит два синтетических счётчика. У planner и validator HTTP-сервера нет вовсе (транспорт запрещён .importlinter) Поднять ops-HTTP у Python-процессов, добавить доменные метрики 01-business-trace.md
NFR-25 Аутентификации нет, факт зафиксирован в ограничениях ЧАСТИЧНО Экрана входа нет (apps/shell/src/App.tsx); перечня известных ограничений как документа нет (см. R-DOC-6) Зафиксировать письменно в перечне ограничений 01-business-trace.md
NFR-27 Секретов в исходниках нет, .env.example у каждого процесса ЧАСТИЧНО gateway/.env.example полон; find src/backend -name '.env.example' — один файл, у planner и validator нет. Секретов в коде не найдено (grep по password/secret/token — только имена переменных) Добавить .env.example остальным процессам 01-business-trace.md
NFR-29, NFR-30 Русский интерфейс и отраслевая терминология ЧАСТИЧНО grep -rni "дрон|БПЛА" src/ — вхождений нет; локаль ru_RU (apps/shell/src/main.tsx:17). Но бэкенд-экспорт пишет английские имена этапов (export/kml.py:47), причины unassigned — английской строкой (pipeline.py:310) Перевести пользовательские строки бэкенда 01-business-trace.md
NFR-31 Единицы СИ с отраслевой формой записи ЧАСТИЧНО schema/units.json и packages/domain/src/format.ts; в бэкенд-отчёте единицы смешаны: checks/g1_airspace.py:35 units="m" против checks/g2_altitude.py:78 units="шт" Привести единицы к одному виду 01-business-trace.md
NFR-35 Перечень ограничений ведётся с первого дня ЧАСТИЧНО docs/task5/TODO.md и «Известные ограничения» в src/frontend/README.md есть; сводного перечня с последствиями для пользователя нет Свести в один документ (см. R-DOC-6) 01-business-trace.md
NFR-36 Репозиторий и демо самодостаточны ЧАСТИЧНО src/backend/README.md:15-17 описывает python -m planner run --scenario scenarios/s01-simple.json, команда падает (No module named planner.main, затем 19 ошибок pydantic) Починить команду и инструкцию 01-business-trace.md
NFR-37 Разрешительные лицензии, сводка актуальна ЧАСТИЧНО Сводка в docs/task5/50-stack/01-libraries.md; сверка с текущими pyproject.toml и package.json не проводилась Сверить список зависимостей со сводкой 01-business-trace.md
R-DEMO-1 Работа сервиса на тестовом сценарии ЧАСТИЧНО Сценарии есть (scenarios/s01..s03), сквозной прогон невозможен (дефект D-1); фронтенд демонстрирует s04 на клиентском расчёте Починить сквозной путь 01-business-trace.md
R-DEMO-2, R-DEMO-3 Визуализация маршрутов и показ результатов оптимизации ЧАСТИЧНО apps/planner/src/map/MapCanvas.tsx, map/layers.ts, components/GanttTimeline.tsx, apps/viewer3d/src/screens/Viewer3DScreen.tsx; данные — клиентский расчёт Подключить к серверному плану 01-business-trace.md
R-DOC-1, R-DOC-2 Описание архитектуры и алгоритма оптимизации ЧАСТИЧНО docs/task5/91-system/01-context.md, 02-services.md, docs/task5/30-domain/03-coverage-path-planning.md, 04-fleet-routing.md — внутренние спецификации; описанный VRP не соответствует реализованному делению на полосы (pipeline.py:361-404) Написать поставочные разделы по факту реализации 01-business-trace.md
R-DOC-3 Инструкция по запуску ЧАСТИЧНО src/backend/README.md и src/frontend/README.md есть; команда планировщика нерабочая, сборки контура одной командой нет См. NFR-09 и NFR-36 01-business-trace.md
R-DOC-4 Описание API ЧАСТИЧНО docs/task5/91-system/03-public-api.md, Swagger на /docs (gateway/src/main.ts:32-40), packages/api-client/openapi.snapshot.json. Описан API, которого нет: /api/scenarios в клиенте (endpoints/scenarios.ts:130-140) без контроллера в app.module.ts:9-15 Синхронизировать документ и реализацию 01-business-trace.md
R-IN-3 Область съёмки ЧАСТИЧНО Перекрёстно: FR-SCN-03 (ГОТОВО), FR-SCN-05/07/09 (ЧАСТИЧНО), FR-SCN-04/06 (НЕТ), FR-TSK-01 (ЧАСТИЧНО) См. соответствующие строки FR-SCN 01-business-trace.md
R-IN-4 Тип съёмки: RGB, мультиспектр, ИК, LiDAR, геофизика ЧАСТИЧНО Перекрёстно: FR-TSK-02/03/06/07, FR-FLT-03 — модель данных полна, отрицательный результат по LiDAR/геофизике не оформлен как ошибка входа См. FR-TSK-07, FR-VAL-02 01-business-trace.md
R-IN-5, R-IN-6 Границы разрешённого ВП и бесполётные зоны ЧАСТИЧНО Перекрёстно: FR-ASP-01…FR-ASP-09, FR-STE-07 — валидатор ГОТОВО, планировщик НЕТ См. FR-PLN-03 01-business-trace.md
R-IN-7 Резервные площадки посадки ЧАСТИЧНО Перекрёстно: FR-STE-04/06/07 — модель есть, использования нет См. FR-PLN-13 01-business-trace.md
R-IN-8 Скорость ветра ЧАСТИЧНО Перекрёстно: FR-MET-01 (ГОТОВО), FR-MET-03/05/06 (ЧАСТИЧНО) См. FR-MET-03 01-business-trace.md
R-OPT-1 … R-OPT-3 Два критерия и выбор критерия в интерфейсе ЧАСТИЧНО Перекрёстно: FR-PLN-17/18/19 — метрики считаются, оптимизации нет (pipeline.py:350-358) См. FR-PLN-17 01-business-trace.md
R-OUT-1 … R-OUT-8 Состав и форматы полётного задания ЧАСТИЧНО Перекрёстно: FR-EXP-01…FR-EXP-12 — KML/GeoJSON формируются, но без скорости, точек старта/посадки, высота только в KML См. строки FR-EXP 01-business-trace.md
R-VEH-1, R-VEH-2 Учёт параметров полёта и объёма заряда ЧАСТИЧНО Перекрёстно: FR-FLT-06/07 и FR-FLT-08/FR-STE-05 — дальность и оборот не учтены См. FR-FLT-06, FR-PLN-12 01-business-trace.md
R-VEH-4 Учёт скорости ветра ЧАСТИЧНО Перекрёстно: FR-MET-02 (ГОТОВО), FR-MET-03 (ЧАСТИЧНО), FR-MET-04 (ГОТОВО в валидаторе) См. FR-MET-03 01-business-trace.md
R-X-1 Резерв заряда ЧАСТИЧНО Перекрёстно: FR-FLT-08 — резерв по времени реализован, энергетической модели нет См. FR-FLT-08 01-business-trace.md
R-X-2, R-X-3 Минимальная безопасная высота и «не ниже точки старта» ЧАСТИЧНО Перекрёстно: FR-FLT-07, BR-11, BR-12 См. FR-FLT-07 01-business-trace.md
R-X-4 Совместимость «борт ↔ полезная нагрузка» ЧАСТИЧНО Перекрёстно: FR-FLT-03 (ГОТОВО), FR-FLT-04, BR-05 (ЧАСТИЧНО в планировщике) См. BR-05 01-business-trace.md
R-X-5 Ограничение по ветру у каждого борта ЧАСТИЧНО Перекрёстно: FR-MET-03, BR-03 — отсечение есть, счётчика доступных бортов нет См. FR-MET-03 01-business-trace.md
R-X-6 Учёт разворотов во времени галса ЧАСТИЧНО Перекрёстно: FR-PLN-08, BR-24 — стоимость разворота в бюджете есть, метрики нулевые См. FR-PLN-08 01-business-trace.md
R-X-7 Поведение при отказе борта в середине задания ЧАСТИЧНО Перекрёстно: FR-MON-01…FR-MON-06, BR-27 См. FR-MON-01 01-business-trace.md
R-X-8 Масштабируемость с числами ЧАСТИЧНО Перекрёстно: FR-PLN-23, FR-RPT-08, NFR-02/NFR-03 — все НЕТ, кроме колонки solve_time в таблице См. FR-RPT-08 01-business-trace.md
NFR-10 Читаемость экранов и контраст не ниже 4,5:1 НЕ ПРОВЕРЕНО Токены палитры есть (packages/tokens/src/tokens.ts, theme.ts), но замер контраста и проверка на 1440×900/1920×1080 требуют браузера; Node/npm в среде отсутствуют (ограничение ревью) Замерить на стенде 01-business-trace.md
FR-VAL-01, FR-VAL-02 Валидация входа до запуска решателя ВНЕ ОБЪЁМА По docs/task5/90-business/05-fr-planning.md:86-87 это вход планировщика. Со стороны валидатора отказ считать оптику реализован: validator/geo/optics.py OpticsUnavailable, тесты validator/tests/test_optics.py:154,161 05-validator.md
R-SUB-1 … R-SUB-9 Артефакты сдачи (концепция, архитектура, прототип, алгоритм, код, демо, презентация, документация, инструкция) ВНЕ ОБЪЁМА Артефакты сдачи, а не код. Из кода подтверждается R-SUB-5 (исходники в репозитории) и частично R-SUB-3 (прототип интерфейса — src/frontend/apps/*) 01-business-trace.md
BR-24 (часть) Инвариант T_план строго больше L/V_g ГОТОВО validator/checks/g6_coverage.py:85-113 + metrics.naive_survey_time_s; тест test_plan_without_turns_fails_turn_time_check 01-business-trace.md
FR-FLT-03 Матрица совместимости как данные, а не как код ГОТОВО docs/task5/10-hardware/payloads.yaml → shared-py/geoscan_contracts/catalog.py (compatibility, payload_covers, model_covers); тест validator/tests/test_contracts.py::test_catalog_compatibility_matrix проходит; GET /api/payloads (gateway/src/fleet/fleet.controller.ts:48-72) 01-business-trace.md
FR-MET-01 Ветер как вектор, метеоконвенция «откуда дует» ГОТОВО shared-py/geoscan_contracts/scenario.py:126-131; planner/planner/geometry/wind.py:15 — wind_to_deg = (wind_from_deg + 180) % 360; независимая реализация в validator/geo/optics.py 01-business-trace.md
FR-MET-02 Время по путевой скорости V_g = sqrt(V_a²−W⊥²)+W∥ ГОТОВО planner/planner/geometry/wind.py:8-34 и validator/geo/optics.py (две независимые реализации); тесты test_ground_speed_tailwind_and_headwind, test_ground_speed_crosswind_only, test_crosswind_gain_matches_theory проходят 01-business-trace.md
FR-MET-04 Допустимость поперечной составляющей ветра на галсе ГОТОВО validator/checks/g4_weather.py:75-112; тест test_crosswind_above_airspeed_is_a_violation 01-business-trace.md
FR-PLN-01 Галсы с шагом из качества, ширины захвата и перекрытия ГОТОВО pipeline.py:321-330 → geometry/optics.py:86-97 → coverage/transects.py:26-103; контрольный шаг 47 м@100 м подтверждён тестом test_pf1b_swath_spacing_at_60_percent 01-business-trace.md
FR-PLN-19 Выбранный критерий отражён в плане ГОТОВО plan.solver.criterion (pipeline.py:487) присутствует в выгруженном JSON плана /tmp/plan_s01.json 04-planner-core.md
FR-PLN-22 Детерминизм расчёта ГОТОВО Два прогона одного сценария, сравнение normalize_plan(...) → (a) deterministic: True; случайности в коде нет (аргумент rng в pipeline.py:276 не используется) 04-planner-core.md
FR-SCN-03 Полигон не менее 3 вершин, расчёт заблокирован до замыкания ГОТОВО packages/domain/src/store.ts:194,218 (draftRing.length < 3 → выход); кнопки заблокированы: apps/planner/src/screens/ScenarioScreen.tsx:193, AirspaceScreen.tsx:94 01-business-trace.md
FR-SCN-07 Правка геометрии в метрической проекции ГОТОВО packages/domain/src/geo.ts:106-136 (rotateRing, scaleRing, translateRing, transformRings) через локальную метрическую проекцию; бэкенд-аналог planner/planner/geometry/projection.py 01-business-trace.md
FR-TSK-05, BR-15 Высота и шаг галсов из GSD и оптики; высота — производная от GSD ГОТОВО planner/planner/geometry/optics.py:39-97, validator/geo/optics.py; прогон validator/tests/test_optics.py: GSD 1,96 см@100 м, footprint 117×78 м, шаг 47 м@100 м и 70,5 м@150 м, altitude_for_gsd обратна gsd 01-business-trace.md
FR-VAL-03 Валидатор не использует код покрытия/распределения, геометрию считает сам ГОТОВО grep -rn "planner" validator/ → только комментарии (geo/init.py:3, tests/fixtures.py:3) и литералы теста; своя геометрия validator/geo/optics.py (225 стр.), geo/projection.py (136), geo/measures.py (130); uv run --with import-linter lint-imports --config .importlinter → «Валидатор не видит планировщик KEPT», при инъекции from planner.geometry import optics в validator/metrics.py → BROKEN: validator.metrics -> planner.geometry.optics (l.167) 05-validator.md
FR-VAL-03, NFR-18 Валидатор не переиспользует код решателя ГОТОВО .importlinter (контракт validator-is-independent) + AST-барьер validator/tests/test_independence.py со сканом импортов, importlib и самопроверкой; все параметризованные тесты проходят (uv run pytest -q → 150 passed) Установить import-linter: uv run lint-imports сейчас не запускается 01-business-trace.md
FR-VAL-04 Жёсткая проверка violations.nfz длиной траектории ГОТОВО validator/checks/g1_airspace.py:26-64; тесты test_no_fly_zone_intrusion_is_measured_in_meters, test_zone_above_flight_level_is_not_a_violation 01-business-trace.md
FR-VAL-05 Жёсткая проверка violations.airspace ГОТОВО validator/checks/g1_airspace.py:67-102; тест test_outside_allowed_airspace_is_measured 01-business-trace.md
FR-VAL-06 Жёсткая проверка violations.endurance с резервом ГОТОВО validator/checks/g3_energy.py:20-61; тест test_endurance_budget_is_checked_with_reserve 01-business-trace.md
FR-VAL-07 Жёсткая проверка violations.wind ГОТОВО validator/checks/g4_weather.py:27-170 (три проверки); тесты test_wind_above_uav_limit_is_a_violation, test_crosswind_above_airspeed_is_a_violation 01-business-trace.md
FR-VAL-08 Жёсткая проверка violations.payload ГОТОВО validator/checks/g5_assignment.py:20-47; тест test_incompatible_payload_is_a_violation 01-business-trace.md
FR-VAL-09 Жёсткая проверка violations.altitude ГОТОВО validator/checks/g2_altitude.py:24-162 (потолок, минимум, ниже старта, соответствие GSD); тест test_altitude_above_ceiling_is_a_violation 01-business-trace.md
FR-VAL-10 Жёсткая проверка violations.turnaround ГОТОВО validator/checks/g3_energy.py:101-145; тест test_turnaround_deficit_is_measured_in_seconds 01-business-trace.md
FR-VAL-11 unassigned пуст либо у каждой записи есть причина ГОТОВО validator/checks/g5_assignment.py:96-153; тест test_unassigned_without_reason_fails 01-business-trace.md
FR-VAL-14 Предупреждения отделены от нарушений ГОТОВО validator/checks/base.py:22-26 (CheckStatus.WARN), admission.py:47-55 (предупреждения не отменяют допуск вне --strict); тест test_ivp_threshold_is_warning_not_violation 01-business-trace.md
FR-VAL-18 (часть) Регрессии против наивных формул: T_план > L/V_g, площадь ±0,1 %, GSD по таблице ГОТОВО checks/g6_coverage.py:85 (G6-02, прогон: «план 2325 с против наивных 1536 с»); validator/tests/test_projection.py:11,25,57 (AREA_TOLERANCE_FRAC=0.001, сверка с geodesic_area_m2); validator/tests/test_optics.py:33-135 05-validator.md
NFR-32 Геометрия в метрической проекции, расхождение площади ≤ 0,1 % ГОТОВО validator/geo/projection.py (UTM с откатом на AEQD), planner/planner/geometry/projection.py; тесты test_projected_area_matches_geodesic, test_projected_length_matches_geodesic, test_s01_polygon_is_about_one_square_kilometer, test_roundtrip_keeps_lon_lat_order — проходят 01-business-trace.md
R-IN-1 Координаты взлётно-посадочных пунктов произвольные ГОТОВО shared-py/geoscan_contracts/scenario.py:23-26 — position: Coordinate без справочника; справочника площадок в коде нет 01-business-trace.md
R-IN-2 Перечень доступных БВС ГОТОВО docs/task5/10-hardware/fleet.yaml → catalog.py → GET /api/fleet (gateway/src/fleet/fleet.controller.ts:22-46, с ETag); тест test_catalog_units_are_converted 01-business-trace.md
R-PLT-3 Стек из рекомендованного списка ГОТОВО src/backend/gateway/package.json (NestJS + Fastify), src/backend/pyproject.toml (Python 3.12, pydantic, shapely, pyproj), src/frontend/package.json (React + Vite); сверка — docs/task5/50-stack/01-libraries.md 01-business-trace.md
R-VEH-3 Учёт скорости ГОТОВО Перекрёстно: FR-FLT-06 и FR-MET-02 — треугольник скоростей реализован дважды, тесты проходят 01-business-trace.md

Системные требования (91-system)

ID Требование Статус Доказательство Что доделать Отчёт
D-02 Очередь — таблица plan_jobs в Postgres + диспетчер в gateway, 2/20, без брокера НЕТ Брокера нет (тривиально), но и таблицы с диспетчером нет: find . -name '*.sql' пусто; grep -rn plan_jobs src/backend → только заглушка gateway/src/plans/plans.controller.ts:34 Таблица plan_jobs, диспетчер, лимиты 2/20, 429 при переполнении 02-system-trace.md
D-03 Сценарии и планы — в Postgres с самого начала, файлового хранилища нет НЕТ Код принял противоположное решение: хранилище — файлы. Сценарии читаются с диска (geoscan_contracts/documents.py:37-48, validator/validator/cli/run.py:53-62), планы пишутся файлами (planner/planner/cli/main.py:41-42,60); Postgres отсутствует целиком Либо реализовать Postgres, либо зафиксировать отступление записью в 11-decisions.md 02-system-trace.md
SR-API-01 Документы сценария и плана — snake_case, гейтвеем не переименовываются НЕТ planner/planner/cli/main.py:40 пишет model_dump_json(by_alias=True); алиасы в shared-py/geoscan_contracts/schema.py — camelCase. Прогон: TOP KEYS ['schemaVersion','scenarioId','generatedAt',…], METRICS KEYS ['makespanS','totalFlightTimeS','coverageFraction',…]; против schema/plan.schema.json — 37 ошибок. Вход тоже camelCase-обязательный: schema.py:110-111 (windSpeedMs без populate_by_name), :129 (timeLimitS), :79 (gsdCm) (также в 06-gateway.md: ЧАСТИЧНО) Планировщик должен читать/писать канонический snake_case-документ (geoscan_contracts.scenario/plan); schema.py удалить 03-contracts.md
SR-API-03 POST /api/scenarios → 201 + Location, сохранение в Postgres НЕТ Контроллера сценариев нет: ls src/backend/gateway/src → config fleet ops plans validate common Ручка + хранилище сценариев 02-system-trace.md
SR-API-03/04 POST/GET /api/scenarios, GET /api/scenarios/{id}, засев демо s01–s10 НЕТ ls src/backend/gateway/src → common, config, fleet, ops, plans, validate (модуля scenarios нет); app.module.ts:9-15 — пять импортов GW-06: модуль scenarios, хранение jsonb, demo-seed 06-gateway.md
SR-API-04 GET /api/scenarios[/{id}], демо s01–s10 сразу после развёртывания НЕТ Ручки нет; ls src/backend/scenarios → s01-simple.json, s02-nfz.json, s03-multi-sites.json (три вместо десяти) Ручки + 7 недостающих демо-сценариев 02-system-trace.md
SR-API-05 Проверка тела по JSON-схеме до вызова домена (400 SCHEMA_VALIDATION_FAILED) НЕТ ajv и ajv-formats объявлены в gateway/package.json:21-22, но grep -rn "ajv\\|Ajv" src/backend/gateway/src → пусто. grep -rn "scenario.schema.json\\|plan.schema.json" src/ → совпадения только внутри самих схем (schema/scenario.schema.json:3, schema/plan.schema.json:3) (также в 02-system-trace.md: ЧАСТИЧНО) Подключить ajv в гейтвее, возвращать errors[] с path (JSON Pointer), rule, message 03-contracts.md
SR-API-07 Опрос GET /api/plans/{id} до терминального статуса; plan только при done НЕТ packages/state/src/api-compute.ts:38 — один getPlan сразу после createPlan, status/progress/error не разбираются; гейтвей src/backend/gateway/src/plans/plans.controller.ts:47-51 всегда отдаёт {planId,status:'queued',message} (также в 02-system-trace.md: ЧАСТИЧНО) Добавить опрос/SSE и обработку queued|running|done|error 07-frontend.md
SR-API-08 SSE GET /api/plans/{id}/events, 4 типа событий, heartbeat 15 с НЕТ grep -rn 'sse|text/event-stream' src/backend/gateway/src — ноль совпадений Поток событий progress/objective/done/error + snapshot 02-system-trace.md
SR-API-10 422 с машиночитаемым details (GSD_UNREACHABLE, несовместимая нагрузка) НЕТ Код объявлен в shared/src/errors/codes.ts:12, но grep -rn GSD_UNREACHABLE src/backend/gateway/src — ноль потребителей Обработка + details.uavs[] по каждому борту 02-system-trace.md
SR-API-11 429 QUEUE_FULL + Retry-After, лимиты 2 running / 20 queued НЕТ Очереди нет; QUEUE_FULL встречается только в реестре кодов shared/src/errors/codes.ts:14 Диспетчер очереди и лимиты 02-system-trace.md
SR-API-14 Idempotency-Key: сохранение полного ответа, 409 при конфликте тела НЕТ grep -rn 'Idempotency' src/backend/gateway/src — ноль; клиент заголовок шлёт (packages/api-client/src/endpoints/plans.ts:13-15) (также в 07-frontend.md: ЧАСТИЧНО) Реестр идемпотентности в одной транзакции с постановкой задачи 02-system-trace.md
SR-API-15 Экспорт 4 форматов, ?uav=, zip, Content-Disposition НЕТ Ручки нет; planner/planner/export/init.py:19 — raise NotImplementedError для .plan и waypoints Ручка + два формата + zip-архив по бортам 02-system-trace.md
SR-API-15/16 Экспорт GET /api/plans/{id}/export/{fmt}, ?uav=, X-Plan-Admitted НЕТ каталога gateway/src/export/ нет; в plans.controller.ts только @Post() (:16) и @Get(':id') (:38) GW-11: трансляция Planner.ExportPlan (proto/geoscan/planner.proto:7), заголовки, 409/404 06-gateway.md
SR-API-15/FE-09 Экспорт через GET /api/plans/{id}/export/{fmt} НЕТ В api-client ручки экспорта нет (endpoints/ = fleet, scenarios, plans, validate); экспорт клиентский — apps/planner/src/screens/ExportScreen.tsx:16 импортирует экспортеры из @geoscan/domain Добавить ручку экспорта и скачивание blob'ом, включая zip и X-Plan-Admitted 07-frontend.md
SR-API-16 Экспорт только при done; X-Plan-Admitted для недопущенного плана НЕТ Ручки экспорта в gateway/src не существует Ручка с проверкой статуса и заголовком допуска 02-system-trace.md
SR-API-17 POST /api/plans/{id}/replan → 202 + parentPlanId НЕТ Маршрута нет в gateway/src/plans/plans.controller.ts; клиент его вызывает — packages/api-client/src/endpoints/plans.ts:33 (также в 07-frontend.md: ЧАСТИЧНО) Ручка перепланирования 02-system-trace.md
SR-API-18 POST /api/validate синхронно отдаёт отчёт валидатора НЕТ gateway/src/validate/validate.controller.ts:24-29 — заглушка {status:'stub'} (также в 07-frontend.md: ЧАСТИЧНО) Прокси на Validator.Validate 02-system-trace.md
SR-CFG-03 PLAN_CONCURRENCY/PLAN_QUEUE_LIMIT/PLAN_DEFAULT_TIME_LIMIT_S/PLAN_SYNC_TIME_LIMIT_S/DEFAULT_SEED из env НЕТ Ни одной переменной в gateway/src/config/config.schema.ts:3-15 и в gateway/.env.example Добавить переменные и их применение 02-system-trace.md
SR-CFG-04 Дефолты .env.example поднимают контур docker compose up офлайн НЕТ ls docker-compose — файла нет (также в 09-security-config.md: ЧАСТИЧНО)* compose + безопасные дефолты 02-system-trace.md
SR-CFG-06 scripts/sync-secrets.sh создаёт Secret geoscan-secrets из creds-store НЕТ ls scripts → bootstrap-graphify.sh, deploy.sh Идемпотентный скрипт синхронизации секретов 02-system-trace.md
SR-CFG-07 Процедура ротации секрета описана и проверена прогоном НЕТ Ни скрипта, ни инструкции в репозитории Описать и проверить ротацию 02-system-trace.md
SR-CFG-08 Один образ на коммит, тег — 8-символьный sha, :latest запрещён НЕТ k3s/geoscan/40-deployment.yaml:27 — image: , тега нет вовсе; compose отсутствует Подставить sha-теги в манифесты и compose 02-system-trace.md
SR-CFG-09 Офлайн-пакет для жюри (образы, compose, .env, PMTiles, шрифты, s01–s10, инструкция) НЕТ ls src/frontend/apps/shell/public → только config.json.example; образов и compose нет Собрать самодостаточный дистрибутив 02-system-trace.md
SR-CFG-12 Базовый адрес API не конфигурируется в рантайме НЕТ Прямо нарушено: apps/shell/src/runtime-config.ts:29-40 — fetch('/config.json') с подстановкой apiBaseUrl; apps/shell/public/config.json.example; packages/state/src/runtime-config.ts:3-20; packages/api-client/src/config.ts:51-58 читает process.env в рантайме (также в 09-security-config.md: ЧАСТИЧНО) Убрать рантайм-конфиг адреса либо зафиксировать пересмотр SR-CFG-12 в 11-decisions.md 02-system-trace.md
SR-CTX-05 Единственный канал выхода — GET /api/plans/{id}/export/{fmt}, 4 формата НЕТ В src/backend/gateway/src нет контроллера экспорта (ls src/backend/gateway/src → config fleet ops plans validate common); экспорт есть только в planner/planner/export/init.py:15-19, форматы .plan и waypoints → NotImplementedError (:19) Ручка экспорта в gateway, форматы .plan и waypoints, zip-выгрузка без ?uav= 02-system-trace.md
SR-CTX-10 Два режима поставки: k3s и офлайн docker compose НЕТ ls docker-compose* — файла нет; k3s/geoscan/40-deployment.yaml:27 — image: docker-compose.yml, реальные манифесты, sha-теги образов 02-system-trace.md
SR-CTX-11 Весь пользовательский сценарий воспроизводим только HTTP-вызовами НЕТ gateway/src/plans/plans.controller.ts:31-35 возвращает заглушку {planId, status:'queued', message:'Очередь plan_jobs и диспетчер — в GW-07'} Реальный конвейер сценарий→план→экспорт 02-system-trace.md
SR-CTX-12 Единственное состояние — Postgres (схемы gateway, airspace) НЕТ find . -name '.sql' -not -path './.git/' — пусто; в gateway/package.json:15-34 нет драйвера БД и ORM Схема БД, таблицы, репозитории 02-system-trace.md
SR-DAT-01 Один Postgres, схема на сервис, свои роли БД НЕТ find . -name '.sql' -not -path './.git/' — пусто; в gateway/package.json нет pg/ORM Postgres, схемы gateway/airspace, роли и гранты 02-system-trace.md
SR-DAT-02 Схема gateway — ровно scenarios/plans/plan_jobs/idempotency_keys + миграции НЕТ Таблиц и миграций нет (find src -type d -name migrations — пусто) Миграции четырёх таблиц 02-system-trace.md
SR-DAT-03 scenarios.body jsonb без нормализации + метаданные НЕТ Таблицы нет; сценарии читаются файлами — geoscan_contracts/documents.py:37-41 Таблица scenarios 02-system-trace.md
SR-DAT-04 plans с body/metrics/validation/fleet_snapshot/parent_plan_id НЕТ Таблицы нет; план CLI пишет файлом — planner/planner/cli/main.py:41-42 Таблица plans 02-system-trace.md
SR-DAT-05 plan_jobs — единственная очередь расчётов, без брокера НЕТ grep -rn plan_jobs src/backend → только строка-заглушка gateway/src/plans/plans.controller.ts:34 Таблица очереди и диспетчер 02-system-trace.md
SR-DAT-06 Диспетчер select … for update skip locked, ≤2 running, ≤20 queued НЕТ Диспетчера нет; переменных PLAN_CONCURRENCY/PLAN_QUEUE_LIMIT нет в config/config.schema.ts:3-15 Диспетчер с блокировками и лимитами 02-system-trace.md
SR-DAT-07 Реестр идемпотентности {scope,key} в одной транзакции с задачей НЕТ Таблицы нет; заголовок Idempotency-Key на сервере не читается Таблица idempotency_keys 02-system-trace.md
SR-DAT-08 Правка сценария с планами создаёт новую версию (group_id, version+1) НЕТ Хранилища сценариев нет Версионирование сценариев 02-system-trace.md
SR-DAT-09 Строка plans неизменяема после записи (права или триггер) НЕТ Таблицы нет Права/триггер на запрет update и delete 02-system-trace.md
SR-DAT-10 Статусная машина plan_jobs + подбор зависших задач при старте НЕТ Таблицы и диспетчера нет; SOLVER_INTERRUPTED объявлен только в shared/src/errors/codes.ts:18 Триггер переходов + аренда задач 02-system-trace.md
SR-DAT-12 Объёмы хранилища: body ≤ 2 МБ, том ≥ 5 ГБ, авточистки нет НЕТ Ни compose, ни PVC — в k3s/geoscan/ нет 30-postgres.yaml Том БД и измерение размеров 02-system-trace.md
SR-DAT-13 (миграции) Изменения схемы только миграциями, имена уникальны в пределах БД НЕТ find src -type d -name migrations — пусто; find . -name '*.sql' — пусто. Примечание: идентификатор SR-DAT-13 задан в спецификации дважды (05-data-model.md:346 и :360), SR-DAT-14 пропущен Завести каталог миграций; починить нумерацию требований 02-system-trace.md
SR-DEP-01 (образы planner/validator) Отдельный образ на каждый процесс НЕТ find . -name 'Dockerfile*' (без node_modules/.venv) → ровно два: src/backend/gateway/Dockerfile и src/frontend/packages/api-client/Dockerfile; для planner и validator Dockerfile'ов нет Добавить Dockerfile'ы для planner и validator 10-build-ops.md
SR-DEP-01 (проверка в CI) CI падает при :latest и при плейсхолдерах НЕТ ls .gitea .github .gitlab-ci.yml → 'No such file or directory' для всех; pipeline-файлов в репозитории нет. Сам grep -rn ':latest' k3s/ чист (exit 1), но проверка не автоматизирована Завести pipeline с проверками :latest, плейсхолдеров, lint, test, сборки образов 10-build-ops.md
SR-DEP-02 (multi-stage) Сборочный тулчейн не попадает в рантайм-слой НЕТ src/backend/gateway/Dockerfile:16 — npm install typescript @nestjs/cli @types/node --no-save; :22 — COPY --from=gateway-build /build/gateway/node_modules ./node_modules целиком, без npm prune --omit=dev Ставить прод-зависимости отдельно (npm ci --omit=dev) или prune перед копированием 10-build-ops.md
SR-DEP-02 (не-root) Процесс в контейнере запускается не от root НЕТ src/backend/gateway/Dockerfile (29 строк, прочитан целиком) — инструкции USER нет ни в одной стадии Добавить USER node (или собственного пользователя) в рантайм-стадию 10-build-ops.md
SR-DEP-02 (сборка образа) Dockerfile сервиса собирается НЕТ docker build -f src/backend/gateway/Dockerfile -t geoscan-gateway:review . → EXIT=1; падение на стадии shared-build: src/backend/shared/src/grpc/client-factory.ts:44 — 'error TS2352: Conversion of type ServiceClient to type T may be a mistake' Починить приведение типа (as unknown as T либо типизировать ServiceCtor), добиться зелёного docker build 10-build-ops.md
SR-DEP-02 / SR-DEP-08 (секреты) В образе нет секретов и .env НЕТ src/backend/gateway/Dockerfile:27 — ENV SERVICE_API_KEY=dev-local-key; buildkit в выводе сборки: 'SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "SERVICE_API_KEY") (line 27)' Убрать дефолт ключа из образа, брать только из secretKeyRef/env контура 10-build-ops.md
SR-DEP-03 Статика фронта в образе gateway, SPA-fallback, относительные адреса remote'ов НЕТ @fastify/static в gateway/package.json:17, но grep -rn 'fastify/static|useStaticAssets' gateway/src — ноль; Dockerfile не копирует фронт; дефолты remote'ов — http://localhost:517x (apps/shell/vite.config.ts:28,33,38) Раздача статики и SPA-fallback, относительные REMOTE_* 02-system-trace.md
SR-DEP-04 Полный состав k3s/geoscan/ без плейсхолдеров, apply -k одной командой НЕТ Каталог — нетронутый шаблон: 40-deployment.yaml:27 — , :30 — ; нет 30-postgres.yaml, 40-deployment-{gateway,planner,validator}.yaml, 70-migrate-job.yaml; scripts/deploy.sh:15-19 откажет при плейсхолдерах Написать полный набор манифестов 02-system-trace.md
SR-DEP-04 (состав) Полный состав манифестов: Postgres, Job миграций, три Deployment/Service НЕТ ls k3s/geoscan/ → только 00-namespace, 10-certificate, 40-deployment, 50-service, 60-ingress, kustomization; diff -r k3s/template k3s/geoscan → расхождения только в подстановке →geoscan Добавить 30-postgres.yaml (StatefulSet+PVC), 70-migrate-job.yaml, манифесты трёх процессов 10-build-ops.md
SR-DEP-07 Postgres StatefulSet + PVC, образ с PostGIS на борту НЕТ ls k3s/geoscan/ — 30-postgres.yaml отсутствует StatefulSet + PVC + образ с PostGIS 02-system-trace.md
SR-DEP-08 Конфигурация только из env, Secret geoscan-secrets из creds-store НЕТ k3s/geoscan/40-deployment.yaml:31-33 — блок env закомментирован; Secret нигде не упоминается secretKeyRef + скрипт создания Secret 02-system-trace.md
SR-DEP-09 Job миграций применяется до выката нового кода НЕТ 70-migrate-job.yaml отсутствует в k3s/geoscan/ Job + порядок выката 02-system-trace.md
SR-DEP-09/10 Job миграций до apply -k, аддитивность миграций НЕТ k3s/geoscan/70-migrate-job.yaml отсутствует (ls k3s/geoscan/); find /home/qovalenko/geoscan-wt/review/src -iname 'migrat' (без node_modules/.venv) → пусто — ни мигратора, ни каталога миграций Реализовать мигратор и Job; решения об отсрочке в docs/task5/91-system/11-decisions.md не зафиксировано 10-build-ops.md
SR-DEP-10 Миграции обратно совместимы в пределах одного выката НЕТ Миграций нет (find . -name '*.sql' — пусто) Правило и проверка при появлении миграций 02-system-trace.md
SR-DEP-11 Фиксированная последовательность выката с нуля + smoke НЕТ Ни инструкции выката, ни smoke-скрипта в репозитории (ls scripts → bootstrap-graphify.sh, deploy.sh) Инструкция и smoke 02-system-trace.md
SR-DEP-13 docker-compose.yml в корне того же состава, что k3s НЕТ ls docker-compose* — файла нет compose того же состава и с теми же образами 02-system-trace.md
SR-DEP-14 Выкат не завершён без scrape-target и дашборда в тот же день НЕТ Каталога k3s/monitoring нет; дашбордов нет (также в 10-build-ops.md: НЕ ПРОВЕРЕНО) Цели и дашборд 02-system-trace.md
SR-NFR-01 time_limit_s ограничивает только фазу решения VRP НЕТ Решателя нет: grep -rn ortools --include=*.toml → только комментарий validator/pyproject.toml:7; planner/planner/pipeline.py:489 лишь переписывает time_limit_s в отчёт Решатель и бюджет именно на фазу решения 02-system-trace.md
SR-NFR-02 По исчерпании бюджета — feasible; timeout только без допустимого решения НЕТ planner/planner/pipeline.py:492 жёстко ставит status='feasible' либо 'feasible_with_unassigned' — значение вне перечня optimal/feasible/infeasible/timeout Корректные статусы решателя 02-system-trace.md
SR-NFR-03 Останов не позднее time_limit_s + 1 с, done за +5/+15 с НЕТ Бюджет решателя не применяется (pipeline.py:478-491 только измеряет фактическое время) Применение и измерение бюджета 02-system-trace.md
SR-NFR-04 Детерминированный досрочный останов на малых задачах НЕТ Решателя и критериев останова нет Критерий останова в конфигурации 02-system-trace.md
SR-NFR-05 Дефолт time_limit_s = 30 с, env-переменная, диапазон 1…600 НЕТ schema/scenario.schema.json требует solver.time_limit_s; env-переменной нет (config/config.schema.ts:3-15), проверки диапазона нет Дефолт, env и валидация 1…600 02-system-trace.md
SR-NFR-06 Демо-сценарий проходит полный цикл за 10–15 с НЕТ Полный цикл не собирается — POST /api/plans заглушка, planner не читает сценарии Собрать цикл и замерить 02-system-trace.md
SR-NFR-07 Исчерпание бюджета в async — не ошибка транспорта НЕТ Асинхронного режима и очереди нет Async-режим и семантика завершения 02-system-trace.md
SR-NFR-08 Диапазон 1…20 бортов, 1…200 км², 1…5 ВПП, 0…20 зон НЕТ В scenarios/ три сценария (s01 ≈1 км², s02 с одной зоной, s03 — 3 борта, 2 ВПП); наборов sc-area и sc-nfz нет Наборы масштабных сценариев и прогоны 02-system-trace.md
SR-NFR-09 Графики времени решения и качества от размера входа + монотонность НЕТ Ни команды сбора, ни данных; в validator/validator/cli нет соответствующей подкоманды Команда сбора и публикация графиков 02-system-trace.md
SR-NFR-10 ≤2 одновременных расчётов, ≤20 ожидающих, оба предела в env НЕТ Очереди нет (см. SR-DAT-05); переменных PLAN_CONCURRENCY/PLAN_QUEUE_LIMIT нет Диспетчер с лимитами 02-system-trace.md
SR-NFR-11 SSE-событие не реже одного раза в 2 с НЕТ SSE-маршрута нет (grep -rn 'text/event-stream' src/backend/gateway/src — ноль) Поток с keep-alive 02-system-trace.md
SR-NFR-13 requests/limits по каждому контейнеру с указанными значениями НЕТ k3s/geoscan/40-deployment.yaml:41-43 — обобщённые 50m/128Mi и 1 CPU/512Mi для единственного контейнера; требуемых planner 2 CPU / 2 Gi нет Манифесты по процессам с заданными ресурсами 02-system-trace.md
SR-NFR-14 Перезапуск не теряет данные и не оставляет висящих задач НЕТ Состояния нет — БД отсутствует; задач нет Хранилище + аренда задач 02-system-trace.md
SR-OBS-02 /readyz = 200 только при готовности зависимостей, иначе 503 с именем зависимости НЕТ gateway/src/ops/ops.controller.ts:26-29 — всегда readyzBody(true), комментарий 'Skeleton: always ready until DB and planner checks land' Реальные проверки БД и доменов 02-system-trace.md
SR-OBS-04 Метрики planner/validator (plan_duration, plan_total, validator_violations_total) НЕТ grep -rn prometheus src/backend — ноль; у Python-процессов нет HTTP-сервера Экспортёры метрик 02-system-trace.md
SR-OBS-05 Метрики gateway: http_request_duration, sse_connections_active, plan_jobs НЕТ ops.controller.ts:42-56 отдаёт две константы-заглушки geoscan_process_up и geoscan_http_requests_total 0 Реальные метрики с шаблонными route-лейблами 02-system-trace.md
SR-OBS-06 Стандартные grpc_server_ / grpc_client_ с лейблом grpc_code НЕТ gRPC-канала в коде нет Интерцепторы метрик обеих сторон 02-system-trace.md
SR-OBS-07 Структурированный JSON-лог в stdout с ts/level/service/request_id/msg НЕТ Логирование обычным console: main.ts:47, common/filters/domain-exception.filter.ts:59, config/config.schema.ts:28 pino на TS и structlog на Python с обязательным request_id 02-system-trace.md
SR-OBS-09 История расчёта одной командой; лейбл app.kubernetes.io/part-of=geoscan НЕТ k3s/geoscan/40-deployment.yaml:12-14 — только labels: { app: geoscan } Общий лейбл и префиксы источника в логах 02-system-trace.md
SR-OBS-10 Scrape-target в k3s/monitoring/prometheus.yml, job geoscan- НЕТ ls k3s → geoscan, template; каталога monitoring не существует Завести цели Prometheus 02-system-trace.md
SR-OBS-11 Дашборд Grafana geoscan минимум из 6 панелей, file-provisioned НЕТ Каталога с дашбордами в репозитории нет JSON-дашборд 02-system-trace.md
SR-OBS-12 Четыре правила алертинга (ServiceDown, GatewayErrors и др.) НЕТ Правил алертинга в репозитории нет Правила вместе со scrape-целью 02-system-trace.md
SR-RPC-02 common.proto: Geometry/Ring/Position, enum статусов, Violation/Warning/Unassigned НЕТ src/backend/proto/geoscan/common.proto — 5 строк, единственное message Empty {} Общие типы + round-trip-тест GeoJSON↔Geometry с точностью 1e-9 02-system-trace.md
SR-RPC-04 Planner.ExportPlan принимает план как данные, не идентификатор НЕТ proto/geoscan/planner.proto:19-23 — message ExportPlanRequest { string plan_id = 1; … } Переписать на передачу плана (bytes plan_json / message Plan) 02-system-trace.md
SR-RPC-05 Validator.Validate: сценарий+план на вход, метрики/нарушения/warnings/unassigned/допуск на выход НЕТ proto/geoscan/validator.proto:9-15 — ValidateRequest{bytes plan_json} без сценария, ValidateResponse{repeated string violations} (также в 03-contracts.md: ЧАСТИЧНО) Привести контракт к форме отчёта валидатора 02-system-trace.md
SR-RPC-08 buf breaking --against main в CI, reserved для удалённых полей НЕТ CI-конфигурации нет (каталогов .github/.gitea не существует); buf не установлен (which buf — пусто) (также в 03-contracts.md: НЕ ПРОВЕРЕНО) CI-шаг buf breaking 02-system-trace.md
SR-RPC-12 Домен пишет x-request-id в каждую строку журнала НЕТ grep -rn 'logging|structlog' src/backend/planner src/backend/validator — ноль; gRPC-серверов нет (также в 03-contracts.md: ВНЕ ОБЪЁМА) Логгер с обязательным полем request_id 02-system-trace.md
SR-RPC-13 Серверный интерцептор проверяет x-service-api-key → UNAUTHENTICATED НЕТ gRPC-серверов в planner/validator нет (нет ни одного grpc.aio.server / add_*_to_server) Серверный интерцептор проверки ключа 02-system-trace.md
SR-RPC-14 Дедлайн на каждом вызове, PLAN_DEADLINE_MARGIN_S по умолчанию 30 НЕТ Переменной нет в gateway/src/config/config.schema.ts:3-15 и в gateway/.env.example; вызовов нет Дедлайны + env-переменная 02-system-trace.md
SR-RPC-16 Ретраи только идемпотентных unary, до 2, backoff 200 мс с джиттером НЕТ gRPC-клиентов в gateway нет (grep -rn createGrpcServiceClient gateway/src — ноль) Политика ретраев на Validate и ExportPlan 02-system-trace.md
SR-SVC-01 Контур ровно gateway+planner+validator(+postgres), объявлен в одном месте НЕТ Нет docker-compose.yml; k3s/geoscan/40-deployment.yaml:6 — единственный обобщённый Deployment name: geoscan compose + k3s по процессам + барьерный тест на перечень 02-system-trace.md
SR-SVC-06 POST /api/validate принимает чужой план и отдаёт 200 с метриками НЕТ gateway/src/validate/validate.controller.ts:24-29 — {status:'stub', message:'Проксирование Validator.Validate будет подключено в GW-12'} Прокси на Validator.Validate, 503 при недоступности 02-system-trace.md
SR-SVC-09 planner запускается из CLI на файле сценария (run --scenario --seed --out) НЕТ uv run python -m planner --help → No module named planner.main; через planner.cli — 'unrecognized arguments: --seed 42'; без --seed — ValidationError: 19 validation errors for Scenario на scenarios/s01-simple.json main.py в корне пакета, флаг --seed, единая модель сценария 02-system-trace.md
SR-SVC-11 airspace — модуль в gateway за интерфейсом из airspace.proto, AIRSPACE_MODE НЕТ grep -rn 'airspace|AIRSPACE' src/backend/gateway src/backend/proto — ноль совпадений Порт, модуль, env-переключатель local/remote 02-system-trace.md
SR-SVC-13 Минимальный контур gateway+planner+postgres проходит сквозной путь НЕТ grep -rn createGrpcServiceClient src/backend/gateway/src — ноль; БД нет; ручки экспорта нет Собрать и прогнать минимальный контур на s01 02-system-trace.md
SR-TST-01 Набор tests/barriers/ с точкой входа scripts/barriers.sh и job barriers в CI НЕТ ls src/backend/tests → integration, conftest.py; ls scripts → bootstrap-graphify.sh, deploy.sh; CI-конфигурации в репозитории нет Завести набор, скрипт и CI-job 02-system-trace.md
SR-TST-03 Барьер «в домене только ops-маршруты» (статика + динамика) НЕТ Теста нет; доменных HTTP-серверов тоже нет Барьер по исходникам + динамическая проверка 02-system-trace.md
SR-TST-04 Барьер сквозного контекста: ни один вызов в домен без metadata НЕТ Теста нет; grep -rn 'x-service-api-key' src/backend → только shared/src/grpc/metadata.ts:9 Статический скан + перехватчик на стороне домена 02-system-trace.md
SR-TST-05 Барьер контракта ошибок: одна таблица, табличный тест по 6 кодам НЕТ Теста нет; таблица продублирована внутри shared/src/grpc/grpc-to-http.ts (константа :4-12 и switch :14-33) Барьер + дедупликация таблицы 02-system-trace.md
SR-TST-06 Барьеры конфигурации и деплоя по репозиторию (Ingress, теги, литералы хостов) НЕТ Тестов нет; k3s/geoscan содержит плейсхолдеры, проверять нечего Статические барьеры 02-system-trace.md
SR-TST-08 (исполнение) Контракт import-linter реально запускается (CI/Makefile) НЕТ grep -c "import-linter" src/backend/uv.lock → 0; cd src/backend && uv run lint-importserror: Failed to spawn: lint-imports; src/backend/Makefile содержит только proto-цели; в корне нет .github/.gitea/.gitlab-ci.yml; ls src/backend/.venv/bin без lint-imports Добавить import-linter в dev-группу pyproject, зафиксировать в uv.lock, завести цель make lint и CI-job 05-validator.md
SR-TST-09 CI-job на чистом venv: только дистрибутив валидатора, прогон CLI на s01–s10 НЕТ CI нет; в scenarios/ три сценария вместо десяти Job + недостающие сценарии 02-system-trace.md
SR-TST-10 (5) makespan не растёт при увеличении числа бортов (набор sc-fleet) НЕТ grep -rn "monoton|sc-fleet" src/backend/{tests,planner/tests,validator/tests} — пусто; фикстур sc-fleet нет Нужен работающий решатель и набор фикстур на 1/2/3/5/10/20 бортов 08-tests.md
SR-TST-10 (6) Два прогона с одним seed дают идентичный план после нормализации НЕТ grep -rn "seed" по тестам даёт только константу seed=42 в фикстурах (tests/integration/conftest.py:72, planner/tests/test_kml_export.py:57); механика нормализации есть и проверена (validator/tests/test_contracts.py:59), но повторного прогона планировщика нет Прогнать pipeline дважды на s04 и сравнить нормализованные дампы 08-tests.md
SR-TST-12 Сверка JSON-схемы и .proto по множеству полей и типов НЕТ Теста нет; сверять нечего — документы в proto передаются как bytes (planner.proto:11, validator.proto:10) Типизировать proto и написать сверку со schema/ 02-system-trace.md
SR-TST-13 Контрактные тесты gateway↔planner и gateway↔validator против реальных процессов НЕТ docker-compose.ci.yml отсутствует; gRPC-процессов нет CI-контур и контрактные тесты по всем RPC 02-system-trace.md
SR-TST-14 Приёмочный прогон s01–s10 по обоим критериям, viol≠0 останавливает сборку НЕТ uv run python -m validator run --scenarios scenarios/ --criteria makespan,total_time → три строки «нет плана», EXIT=0 (validator/validator/cli/run.py:148-154 при пустом списке отчётов возвращает EXIT_OK) (также в 08-tests.md: ЧАСТИЧНО) Сценарии, планировщик, гейт в CI, ненулевой код при отсутствии планов 02-system-trace.md
SR-TST-15 E2E-smoke по публичному REST на контуре gateway+planner НЕТ Ручек сценариев, SSE и экспорта нет (см. SR-API-03…08, SR-API-15) Smoke-скрипт 02-system-trace.md
SR-TST-16 Тест идемпотентности: один расчёт, байт-в-байт повтор, 409 на другое тело НЕТ Идемпотентности на сервере нет (grep -rn Idempotency gateway/src — ноль) Реализация + тест 02-system-trace.md
D-01 Аутентификации пользователей нет; форма A-04 (metadata на доменных вызовах) сохраняется ЧАСТИЧНО Отрицательная часть выполнена: в gateway/package.json:15-34 нет passport/jsonwebtoken, guard'ов в gateway/src нет, процесса auth нет. Положительная часть — buildMetadata (shared/src/grpc/metadata.ts:3-14) не вызывается ниоткуда Проставлять metadata при появлении доменных вызовов 02-system-trace.md
D-04 export/ — модуль planner; наружу RPC Planner.ExportPlan, принимающий план как данные ЧАСТИЧНО Модуль на месте и вызывается функцией от плана (planner/planner/export/init.py:8-19). Но контракт принимает идентификатор: proto/geoscan/planner.proto:19-23 — string plan_id = 1. Плюс вторая реализация KML на фронте: src/frontend/packages/domain/src/exporters.ts:130 Переписать ExportPlanRequest на план как данные; убрать дубль экспорта на фронте 02-system-trace.md
D-05 PostGIS на старте не включается, расчёты Shapely/pyproj; образ Postgres — сразу с PostGIS ЧАСТИЧНО Расчётная половина принята именно так: validator/validator/geo/projection.py, measures.py, planner/planner/geometry/projection.py; зависимости validator/pyproject.toml:11-13 и planner/pyproject.toml:7-9; PostGIS не используется. Вторая половина не реализована — манифеста Postgres нет (ls k3s/geoscan/ без 30-postgres.yaml) Манифест Postgres с образом, содержащим PostGIS 02-system-trace.md
SR-API-02 x-request-id в каждом ответе, включая ошибочный, и в metadata доменного вызова ЧАСТИЧНО common/interceptors/request-id.interceptor.ts:20-27 ставит заголовок, но интерцептор не срабатывает для несопоставленных маршрутов и для 413 (FST_ERR_CTP_BODY_TOO_LARGE обрабатывается фильтром); в metadata и в лог не уходит Перенести на хук Fastify, прокинуть в metadata и логи 02-system-trace.md
SR-API-06 POST /api/plans → 202 {planId,status,scenarioId,queuePosition} + Location ЧАСТИЧНО plans.controller.ts:16-36 отдаёт 202 и planId, но без scenarioId, queuePosition и заголовка Location; очереди и синхронного режима при time_limit_s ≤ 5 нет Диспетчер, полный конверт ответа, Location, синхронный режим 02-system-trace.md
SR-API-09 Тело ошибки {errorCode,message,details?,errors?}; таблица gRPC→HTTP в одном месте ЧАСТИЧНО Форма тела — shared/src/grpc/grpc-to-http.ts:41-46. Но соответствие кодов записано дважды в одном файле: GRPC_TO_HTTP (:4-12, мёртвая константа) и switch в grpcToHttp (:14-33) Оставить один экземпляр таблицы, добавить табличный тест по 6 кодам 02-system-trace.md
SR-API-09…13 Единый разбор ошибок 400/422/429/503 в ApiError ЧАСТИЧНО client.ts:84-93 строит ApiError из тела с errorCode, иначе INTERNAL. Но тело парсится до проверки res.ok (client.ts:81-82) — не-JSON ответ даёт SyntaxError; Retry-After не читается; details.uavs[] (s08) не разбирается; ни один экран не показывает errorCode Разбор по кодам, устойчивый парсинг, экран 422 07-frontend.md
SR-API-12 504 UPSTREAM_TIMEOUT только в синхронных режимах ЧАСТИЧНО grpc-to-http.ts:21-22 — DEADLINE_EXCEEDED → 504; дедлайнов, синхронных режимов и plan.solver.status='timeout' нет Дедлайны вызовов и режимы работы 02-system-trace.md
SR-API-13 503 UPSTREAM_UNAVAILABLE с details.service, 3 попытки → error ЧАСТИЧНО grpc-to-http.ts:26-27 — UNAVAILABLE → 503; details.service не заполняется, ретраев нет gRPC-клиенты, details.service, политика 3 попыток 02-system-trace.md
SR-API-19 GET /api/fleet и /api/payloads: три борта, reference_only скрыт, ETag+Cache-Control ЧАСТИЧНО fleet/fleet.controller.ts:31-46 отдаёт YAML как есть с ETag; include_reference игнорируется (:33 — _includeReference); docs/task5/10-hardware/fleet.yaml:150,169 содержит reference_only: true у geoscan_401_geodesy и geoscan_701 → в ответе 5 моделей Фильтрация reference_only, Cache-Control, сборка на этапе образа 02-system-trace.md
SR-API-20 OpenAPI из кода, /docs и /docs-json, покрывает все маршруты и заголовки ЧАСТИЧНО main.ts:32-40 — SwaggerModule.setup('docs', …, {jsonDocumentUrl:'docs-json'}); покрыты только 6 маршрутов; заголовки Idempotency-Key и x-request-id не описаны; packages/api-client/openapi.snapshot.json содержит 4 пути Описать заголовки и недостающие ручки, барьер число операций = число маршрутов 02-system-trace.md
SR-CFG-01 Конфигурация только из env одним типизированным слоем, fail-fast ЧАСТИЧНО TS: config/config.schema.ts:3-15 (zod) + parseEnvConfig с process.exit(1) (:23-30), ConfigService. Python-слоя нет (grep -rn 'BaseSettings|pydantic_settings' src/backend — ноль); диапазон HTTP_PORT 1…65535 не задан Settings на pydantic для planner/validator, проверка диапазонов 02-system-trace.md
SR-CFG-02 .env.example у каждого процесса + тест test_env_example ЧАСТИЧНО find . -name '.env.example' -not -path './.venv/*' → только ./gateway/.env.example; теста-сканера нет .env.example для planner/validator + сканер ключей по исходникам 02-system-trace.md
SR-CFG-05 Продовые секреты только в creds-store, в git и образах — ничего ЧАСТИЧНО .env* в .gitignore, в репозитории только .env.example с dev-префиксом (gateway/.env.example:2); но gateway/Dockerfile:27 вшивает ENV SERVICE_API_KEY=dev-local-key в образ Убрать ENV из Dockerfile 02-system-trace.md
SR-CFG-10 Ноль исходящих запросов из контура в рантайме ЧАСТИЧНО Python-домены чисты: planner/pyproject.toml:6-11, validator/pyproject.toml:8-13 (shapely/pyproj/numpy); барьер validator/tests/test_independence.py:16 прогнан — uv run pytest … -q → 56 passed in 0.15s. Декларативной части нет: compose с internal: true отсутствует, NetworkPolicy в k3s/geoscan/ нет (также в 02-system-trace.md: НЕ ПРОВЕРЕНО) Compose-сеть internal: true и NetworkPolicy (SEC-R-13) 09-security-config.md
SR-CFG-11 Офлайн-активы (PMTiles подложка и рельеф, локальные woff2) с того же origin ЧАСТИЧНО git grep -nE 'https?://' -- src/frontend/apps src/frontend/packages — только комментарии, KML-namespace и localhost-дефолты REMOTE_*; самих активов нет: ls src/frontend/apps/shell/public → config.json.example Вендорить PMTiles и шрифты в дистрибутив 02-system-trace.md
SR-CTX-01 Наружу публикуется только gateway, домены — ClusterIP ЧАСТИЧНО k3s/geoscan/60-ingress.yaml:24-32 — один Ingress, но на обобщённый сервис geoscan; отдельных planner/validator/postgres нет; docker-compose.yml отсутствует Манифесты по процессам, compose, автотест на секцию ports 02-system-trace.md
SR-CTX-02 Периметр — ровно gateway/planner/validator/airspace/postgres + статика ЧАСТИЧНО k3s/geoscan/kustomization.yaml:5-10 — один Deployment; grep -rn airspace src/backend — ноль совпадений; перечень процессов нигде не объявлен Объявить состав в одном месте + барьерный тест 02-system-trace.md
SR-CTX-03 Фронт с того же origin, только /api, CORS не нужен ЧАСТИЧНО CORS не включён (в gateway/src/main.ts нет enableCors); но packages/api-client/src/client.ts:32 разрешает абсолютные http(s)://, а packages/api-client/src/config.ts:51-60 берёт базу из VITE_GEOSCAN_API_BASE Убрать абсолютные URL и рантайм-базу, тест на отсутствие Access-Control-Allow-Origin 02-system-trace.md
SR-CTX-04 Единственный вход — 11 маршрутов REST /api + OpenAPI на /docs ЧАСТИЧНО Зарегистрировано 6: plans.controller.ts:16,38; validate.controller.ts:7; fleet.controller.ts:22,48. Нет scenarios, events, export, replan 5 недостающих маршрутов + барьер на снимок списка 02-system-trace.md
SR-CTX-06 Ноль обращений за периметр в рантайме ЧАСТИЧНО git grep -nE 'https?://' -- src/frontend/apps src/frontend/packages — только комментарии, KML-namespace и localhost-дефолты REMOTE_* (apps/shell/vite.config.ts:28,33,38); офлайн-прогон невозможен — контур не поднимается Офлайн-прогон демо-сценария, политика deny egress 02-system-trace.md
SR-CTX-07 Ветер, ВП и бесполётные зоны — только из сценария ЧАСТИЧНО schema/scenario.schema.json содержит weather и airspace; сетевых клиентов к метео/ЕС ОрВД нет (grep -rn 'requests|httpx' src/backend — ноль) Строки в перечне известных ограничений; сценарии s06/s09 в репозитории отсутствуют 02-system-trace.md
SR-CTX-08 Ops-контракт /healthz,/readyz,/status,/metrics у каждого процесса ЧАСТИЧНО gateway/src/ops/ops.controller.ts:18-56 — все четыре; grep -rn 'healthz' src/backend/planner src/backend/validator — ноль Ops-HTTP в planner и validator 02-system-trace.md
SR-CTX-09 Секреты пересекают границу только на этапе развёртывания ЧАСТИЧНО .env в .gitignore, есть gateway/.env.example; но gateway/Dockerfile:27 — ENV SERVICE_API_KEY=dev-local-key вшит в рантайм-слой Убрать ENV из Dockerfile, .env.example для planner/validator 02-system-trace.md
SR-DAT-11 Геометрия — GeoJSON в jsonb WGS84, без PostGIS, расчёты Shapely/pyproj в памяти ЧАСТИЧНО Расчётная часть выполнена: validator/validator/geo/projection.py:1-136 (UTM/AEQD по центроиду), validator/tests/test_projection.py:25-33 — площадь в проекции = геодезической в пределах 0,1 %; хранения нет — БД отсутствует Хранение геометрии в jsonb 02-system-trace.md
SR-DEP-01 Отдельный образ на процесс, тег — 8-символьный sha, latest запрещён ЧАСТИЧНО Dockerfile есть только у gateway (src/backend/gateway/Dockerfile) и у фронтового packages/api-client/Dockerfile; у planner и validator их нет; :latest не встречается, но и реальных тегов нет Dockerfile для Python-процессов, sha-теги 02-system-trace.md
SR-DEP-01 (теги образов) 8-символьный sha-тег, :latest запрещён ЧАСТИЧНО grep -rn ':latest' k3s/ → ноль вхождений (exit 1) — требование формально не нарушено; но тега нет вовсе: k3s/geoscan/40-deployment.yaml:25 — 'image: '. Образ ни разу не собран (docker build падает), docker image inspect проверять не на чем Подставить реальный sha-тег после починки сборки 10-build-ops.md
SR-DEP-02 Multi-stage, не от root, 0.0.0.0, без секретов и .env в образе ЧАСТИЧНО Multi-stage ✓ (gateway/Dockerfile:3,7,18), слушает 0.0.0.0 ✓ (main.ts:45); но USER не задан (root) и Dockerfile:27 — ENV SERVICE_API_KEY=dev-local-key USER node, убрать секрет из образа 02-system-trace.md
SR-DEP-05 Наружу только gateway; nginx, force-ssl-redirect, whitelist, TLS ЧАСТИЧНО k3s/geoscan/60-ingress.yaml:8-11,17-19 — force-ssl-redirect, whitelist 100.64.0.0/10,10.42.0.0/16, класс nginx, TLS geoscan-tls; но backend — обобщённый сервис geoscan, сервисов planner/validator/postgres нет (также в 10-build-ops.md: ГОТОВО) Сервисы по процессам, Ingress на gateway 02-system-trace.md
SR-DEP-06 livenessProbe → /healthz, readinessProbe → /readyz, порты 3000/3001/3002 ЧАСТИЧНО k3s/geoscan/40-deployment.yaml:32-33 — readinessProbe указывает на /healthz вместо /readyz; порт — плейсхолдер Исправить путь readiness, задать порты по процессам 02-system-trace.md
SR-DEP-08 (.env.example) У каждого сервиса есть .env.example с полным перечнем переменных ЧАСТИЧНО find src/backend -name '.env.example' (без .venv) → только src/backend/gateway/.env.example (11 переменных); для planner и validator файлов нет Завести .env.example для planner и validator 10-build-ops.md
SR-DEP-12 Обновление и откат сменой sha-тега + ./scripts/deploy.sh с ожиданием всех Deployment ЧАСТИЧНО scripts/deploy.sh:26-31 делает apply -k --server-side и ждёт rollout только одного deploy/$NAME; тегов образов нет Ожидание rollout по всем Deployment namespace, проставить теги 02-system-trace.md
SR-NFR-16 Одинаковый вход и seed → идентичный план и байты выгрузок ЧАСТИЧНО Инфраструктура сравнения готова: shared-py/geoscan_contracts/normalize.py:13-19 (VOLATILE_FIELDS) и тест validator/tests/test_contracts.py:59-67. Самого сравнения двух прогонов нет; seed в PlanOptions отсутствует (shared-py/geoscan_contracts/schema.py:272-278), CLI его не принимает Прокинуть seed, тест на два прогона с побайтовым сравнением 02-system-trace.md
SR-OBS-01 Ровно четыре ops-маршрута нужной формы; посторонние — 404 ЧАСТИЧНО gateway/src/ops/ops.controller.ts:18,24,31,42 — четыре маршрута, Content-Type метрик верный (:54). Но тело /readyz — {ready:true} (shared/src/ops-http-contract.ts:36-38) вместо {'status':'ok'}/503; у planner и validator ops-маршрутов нет Привести форму /readyz, добавить ops в Python-процессы 02-system-trace.md
SR-OBS-03 /status одной формы, собирается одной общей функцией на язык ЧАСТИЧНО TS выполнено: shared/src/ops-http-contract.ts:10-25 отдаёт {service,version,commit,bootTime,uptimeSec,timestamp}, используется в ops.controller.ts:34-39; общей функции для Python нет Аналог в shared-py 02-system-trace.md
SR-OBS-08 x-request-id рождается на gateway, валидируется по регэкспу, уходит в metadata и лог ЧАСТИЧНО request-id.interceptor.ts:20-27 берёт входящий или генерирует UUIDv4 и возвращает в заголовке; регэксп ^[A-Za-z0-9_.:-]{8,128}$ не проверяется, в metadata и журнал ничего не уходит Валидация формата, проброс в metadata и логи 02-system-trace.md
SR-RPC-01 4 .proto в proto/geoscan, пакеты …v1, без рукописных дублей структур ЧАСТИЧНО Пакеты корректны: planner.proto:3, validator.proto:3, common.proto:3; файлов три — airspace.proto нет (согласуется с SR-RPC-06, противоречит букве SR-RPC-01); buf lint не выполнялся (which buf — пусто) Снять противоречие SR-RPC-01 ↔ SR-RPC-06, запустить buf lint в CI 02-system-trace.md
SR-RPC-02 (критерий round-trip) GeoJSON-полигон с вырезами → Geometry → GeoJSON с точностью 1e-9 ЧАСТИЧНО tests/integration/test_geometry_contract_roundtrip.py:73-98 проходит (в составе uv run pytest -q → 150 passed): Polygon с вырезом, MultiPolygon, LineString, abs=1e-9. Но это round-trip pydantic-моделей geoscan_contracts.geometry, а не proto-сообщения Geometry, которого не существует Повторить тест через сгенерированный Geometry после наполнения common.proto 03-contracts.md
SR-RPC-03 Planner.Plan — server-streaming, progress со стадией/долей/целевой функцией, один result ЧАСТИЧНО planner.proto:6 — returns (stream PlanEvent); но PlanEvent (:14-17) = {string kind; bytes payload_json} — ни стадии, ни доли, ни значения целевой функции; сервера нет Типизировать события, реализовать серверную часть 02-system-trace.md
SR-RPC-06 Пока airspace внутри gateway — он за портом AirspacePort ЧАСТИЧНО airspace.proto корректно отсутствует; но grep -rn AirspacePort src/backend — ноль совпадений Завести порт AirspacePort в gateway 02-system-trace.md
SR-RPC-07 Единые правила именования, суффиксы единиц, *_UNSPECIFIED у enum ЧАСТИЧНО Поля snake_case (planner.proto:11,20-22), но ни одного enum в контракте нет; buf lint не запускался Добавить enum'ы с нулевым UNSPECIFIED и линт в CI 02-system-trace.md
SR-RPC-09 (A-03) keepCase/longs/arrays одной общей функцией + pin-тест по всем файлам с регистрацией клиентов и серверов ЧАСТИЧНО Функция полна: shared/src/grpc/loader-options.ts:4-13 — keepCase: true, longs: Number, enums: String, defaults: true, oneofs: true, arrays: true. Единственный вызов загрузчика через неё: shared/src/grpc/client-factory.ts:17-20 (grep -rn "protoLoader\\|loadSync" src/backend даёт только эти два файла). Флаги генератора совпадают: gateway/package.json:6. Но pin-тест tests/integration/test_loader_options.py:28-47 читает ровно два файла и сверяет подстроки — обхода дерева исходников нет, функциональной проверки (snake_case/int64/пустой repeated) нет. Регистраций клиентов сегодня ноль: grep -rn "createGrpcServiceClient\\|loadPackageDefinition" gateway/src → пусто Переписать барьер на обход всех исходников; добавить функциональный тест на трёх типах полей после появления сервера 03-contracts.md
SR-RPC-10 Кодогенерация в CI и в сборке образа, генерат не в git ЧАСТИЧНО .gitignore:59-64 игнорирует генерат; Makefile:4-8 и gateway/package.json:6 (флаги --keepCase --longs=Number --enums=String --defaults --oneofs --arrays); но uv run python -c 'import grpc_tools' → ModuleNotFoundError, grpcio-tools нигде не объявлен, proto-gen не входит в members (pyproject.toml:4) Объявить grpcio-tools, включить proto-gen в uv-workspace 02-system-trace.md
SR-RPC-11 x-request-id и x-service-api-key в metadata каждого вызова, общей функцией ЧАСТИЧНО shared/src/grpc/metadata.ts:3-14 — функция есть; grep -rn buildMetadata src/backend/gateway/src — ноль вызывающих Подключить при появлении gRPC-клиентов 02-system-trace.md
SR-RPC-15 GRPC_MAX_MESSAGE_BYTES = 64 МиБ явно на приём и передачу в обоих языках ЧАСТИЧНО TS: config/config.schema.ts:14 (67_108_864) и shared/src/grpc/client-factory.ts:10-13 — обе границы; Python-стороны нет Те же лимиты в Python-серверах + тест размера PlanResult 02-system-trace.md
SR-SVC-02 gateway — единственный публичный, владелец состояния, без геометрии ЧАСТИЧНО gateway/package.json:15-34 — нет @turf/*, jsts, proj4, geolib (deny-список соблюдён); владения состоянием нет — БД не подключена Хранилище сценариев/планов/очереди; барьерный тест на deny-список 02-system-trace.md
SR-SVC-03 planner без состояния и доступа к БД, по HTTP — только ops ЧАСТИЧНО planner/pyproject.toml:5-10 — только geoscan-contracts/shapely/pyproj/numpy, БД нет; ops-HTTP и .env.example отсутствуют Ops-HTTP, .env.example, тест повторяемости по seed 02-system-trace.md
SR-SVC-05 Независимость валидатора обеспечена топологией ЧАСТИЧНО validator/pyproject.toml:7-14 — без geoscan-planner и ortools; validator/tests/test_independence.py:15-37 запрещает корни planner/ortools и транспорт (150 тестов зелёные). Оркестрации gateway и трассы по x-request-id нет Оркестрация Planner.Plan + Validator.Validate из gateway с общей трассой 02-system-trace.md
SR-SVC-07 Ядро planner: geometry/coverage/routing/export, без циклов, барьер на граф импортов ЧАСТИЧНО ls src/backend/planner/planner → baseline cli coverage export geometry pipeline.py — модуля routing/ нет; .importlinter:41-55 описывает слои только для validator Модуль маршрутизации, контракт слоёв planner в import-linter 02-system-trace.md
SR-SVC-08 Ядро не знает про HTTP/gRPC, gRPC — тонкий адаптер поверх функций CLI ЧАСТИЧНО Для validator закрыто AST-тестом validator/tests/test_independence.py:16,29-37; .importlinter:20-38 источниками указывает только validator и geoscan_contracts — planner не покрыт; gRPC-адаптера нет Барьер для planner, адаптер, тест эквивалентности CLI ↔ RPC 02-system-trace.md
SR-SVC-14 Новый процесс — только по критерию соразмерности с обоснованием ЧАСТИЧНО Лишних процессов нет — но и манифестов по процессам нет (k3s/geoscan/kustomization.yaml:5-10); барьерного теста из SR-SVC-01 не существует Барьер на перечень процессов 02-system-trace.md
SR-TST-02 Pin-тест опций загрузчика: AST-скан всех регистраций клиентов и серверов ЧАСТИЧНО tests/integration/test_loader_options.py:28-47 — текстовая сверка строк 'keepCase: true', 'longs: Number' и флагов proto:gen; AST-скана регистраций нет, ts-morph в зависимостях отсутствует Заменить grep на AST-скан по исходникам 02-system-trace.md
SR-TST-07 Пакетный барьер: wheel валидатора, Requires-Dist без planner, allow-list ЧАСТИЧНО Структура выполнена: src/backend/validator/pyproject.toml — отдельный дистрибутив geoscan-validator, зависимости geoscan-contracts/shapely/pyproj/numpy, planner отсутствует; корневой src/backend/pyproject.toml:1-4 намеренно без [project]. Теста нет: grep -rn "Requires-Dist|wheel|build" по тестам — совпадений нет (также в 02-system-trace.md: ГОТОВО) Тест test_validator_distribution: собрать wheel, разобрать метаданные, сверить с allow-list 08-tests.md
SR-TST-08 Статический барьер импортов: import-linter forbidden + AST-скан обходных путей ЧАСТИЧНО AST-скан работает: validator/tests/test_independence.py:29-54 + самопроверка :57-68. Контракт .importlinter:7-19 написан, но uv run lint-imports → 'Failed to spawn: lint-imports'; зависимости нет ни в одном pyproject.toml Добавить import-linter в dev-зависимости и в CI 02-system-trace.md
SR-TST-08 (self-check 6 из 6) Самопроверка барьера на шести обходных путях импорта ЧАСТИЧНО /tmp/bypass2 с exec("import planner as _p"), sys.path.insert и builtins"import" ("planner") в validator/metrics.py → uv run pytest /tmp/bypass2/tests/test_independence.py -q → 56 passed. Ловятся только статические импорты (test_independence.py:29) и вызовы по имени import_module/import (:41) Добавить в скан exec/eval/compile, присваивания sys.path, вызовы через Subscript; test_scan_catches_injected_bypass:56 должен вызывать саму функцию скана 05-validator.md
SR-TST-10 Шесть регрессий модели времени, проекции и детерминизма ЧАСТИЧНО Есть: площадь в проекции (validator/tests/test_projection.py:25-33), порядок lon/lat (:43-54), контрольные числа GSD/footprint/шага (validator/tests/test_optics.py:36-120). Проверка T ≥ L/V_g реализована как G6-02 (validator/validator/checks/g6_coverage.py:86-112), но отдельного теста нет (grep -n 'G6-02' validator/tests/test_checks.py — пусто). Монотонности makespan и воспроизводимости по seed нет Тесты 1, 5 и 6 из шести 02-system-trace.md
SR-TST-10 (1) T_план > L_total / V_g строго, turn_time_fraction > 0, на s01–s10 по обоим критериям ЧАСТИЧНО Проверка реализована как G6-02 — validator/validator/checks/g6_coverage.py:86-112, наивная оценка validator/validator/metrics.py:100. Ассертится косвенно: validator/tests/test_report_and_cli.py:65-74 (G6-02 присутствует в отчёте) и test_checks.py:23 (нет FAIL на чистом плане); turn_time_frac > 0 — test_checks.py:42 Нет негативного теста (план с T == naive → FAIL) и нет прогона на s01–s10 08-tests.md
SR-TST-10 (3) Центроид любой выгрузки внутри bbox входной области; тест падает при перестановке lon/lat ЧАСТИЧНО validator/tests/test_projection.py:43-54 — центроид после прямого/обратного проецирования полигона сценария возвращается в bbox кольца Нет проверки центроида по содержимому экспортированных файлов (GeoJSON/KML) и нет мутационного случая с перестановкой координат 08-tests.md
SR-TST-11 (1) В каждом LineString KML есть altitudeMode, координаты трёхмерные — по s01–s10 и всем бортам ЧАСТИЧНО planner/tests/test_kml_export.py:78 (altitudeMode relativeToGround в каждом блоке LineString, высота в координатах) и tests/integration/test_kml_roundtrip.py:24-34 (для каждого LineString altitudeMode + каждый токен координат — тройка) Оба теста гоняются на одном синтетическом плане (tests/integration/conftest.py:21) только с survey-этапами; нет прогона по сценариям и нет мутации «удалили altitudeMode» 08-tests.md
SR-TST-11 (2) Round-trip экспорт → импорт: геометрия, набор и порядок этапов, число галсов; по 4 форматам; RFC 7946 / XSD ЧАСТИЧНО tests/integration/test_kml_roundtrip.py:40 — координаты с допуском 1e-7°, высоты 0,1 м, число линий совпадает; tests/integration/test_geojson_roundtrip.py:26 — координаты 1e-7° + altitude_frame == AGL Обратного конвертера нет (сравнение с in-memory планом); набор и порядок этапов не сверяются — conftest.py:99 отбрасывает всё кроме survey; форматы plan и waypoints — NotImplementedError (planner/planner/export/init.py:19); форматной валидации RFC 7946 и XSD нет 08-tests.md
SR-NFR-12 gateway остаётся отзывчивым во время расчётов НЕ ПРОВЕРЕНО Расчётного пути не существует (plans.controller.ts — заглушка), нагрузочный прогон невозможен Поднять контур и выполнить нагрузочный тест 02-system-trace.md
SR-NFR-15 Время старта до /readyz: gateway ≤20 с, planner ≤30 с, validator ≤20 с НЕ ПРОВЕРЕНО Образов planner/validator нет, контур не поднимается; Node/npm в среде отсутствует — gateway собрать нечем Собрать образы и замерить старт 02-system-trace.md
SR-API-15 / SR-API-16 REST-контракт выгрузки: четыре формата, Content-Type, ?uav=, zip, X-Plan-Admitted ВНЕ ОБЪЁМА Контрактная часть покрыта строкой SR-RPC-04. Маршрут /api/plans/{id}/export/{fmt} не зарегистрирован: gateway/src/plans/plans.controller.ts:16,38 — только POST /api/plans и GET /api/plans/:id. Оценка REST-поведения — за ревью гейтвея 03-contracts.md
SR-DAT-13 (зоны airspace) Схема airspace с таблицей зон — при выделении процесса ВНЕ ОБЪЁМА Триггер не сработал; D-05 — docs/task5/91-system/11-decisions.md:59 02-system-trace.md
SR-SVC-12 Выделение airspace в отдельный процесс — по триггеру ВНЕ ОБЪЁМА Триггер не сработал; решение зафиксировано docs/task5/91-system/11-decisions.md:59 (D-05) и 04-internal-contract.md:94 (SR-RPC-06) 02-system-trace.md
SR-DEP-04 (сертификат) Certificate geoscan-tls от ClusterIssuer ff-ca-issuer ГОТОВО k3s/geoscan/10-certificate.yaml:5-15 — name geoscan-tls, secretName geoscan-tls, dnsNames [geoscan.ff], issuerRef {name: ff-ca-issuer, kind: ClusterIssuer}; kubectl kustomize k3s/geoscan/ → EXIT=0 10-build-ops.md
SR-RPC-09 Опции загрузчика proto — одной общей функцией, без инлайновых объектов ГОТОВО shared/src/grpc/loader-options.ts:4-13 — единственная функция; grep -rn 'protoLoader.load' src/backend → только shared/src/grpc/client-factory.ts:17, вызывает createProtoLoaderOptions() 02-system-trace.md
SR-RPC-10 (часть «не коммитить сгенерированное») git не содержит _pb2.py, .desc и вывода генерации proto ГОТОВО git ls-files \| grep -E "_pb2\|\.desc" → пусто; .gitignore:52-56 и gateway/.gitignore:3. Оговорка: в git лежат src/frontend/packages/domain/src/*.d.ts (11 файлов), но это довоенный фронтенд, к каталогам proto-генерации отношения не имеющий 03-contracts.md
SR-SVC-04 validator пересчитывает метрики и нарушения заново по геометрии плана ГОТОВО uv run python -m validator check --scenario /tmp/s01.json --plan /tmp/p01.json → проверки G1-01…G6-03 с пересчитанными значениями; реализация validator/validator/checks/g1_airspace.py, g6_coverage.py, metrics.py:100; импортов planner нет (test_independence.py) 02-system-trace.md
SR-SVC-10 CLI валидатора существует раньше gRPC и работает без grpcio ГОТОВО uv run python -m validator run --scenarios scenarios/ --criteria makespan,total_time печатает сводную таблицу; check — подробный отчёт; validator/pyproject.toml:7-14 — grpcio отсутствует 02-system-trace.md
SR-TST-10 (2) Площадь в метрической проекции ≈ геодезической, ≤ 0,1 % ГОТОВО validator/tests/test_projection.py:25-33 — projected_m2 == approx(geodesic_m2, rel=0.001) на полигоне s01; :57-63 то же для длины; :14-22 выбор UTM/AEQD; :36-40 площадь s01 ≈ 1 км² 08-tests.md
SR-TST-10 (4) Контрольные числа проверяются раздельно для геометрии planner и геометрии validator ГОТОВО Две независимые реализации и два независимых теста: planner/planner/geometry/optics.py ↔ planner/tests/test_geometry_optics.py и validator/validator/geo/optics.py ↔ validator/tests/test_optics.py; общего кода формул нет — uv run --with import-linter lint-imports: контракт «Валидатор не видит планировщик» KEPT 08-tests.md
SR-TST-11 Две регрессии выгрузки ПЗ: altitudeMode и 3D-координаты, round-trip геометрии ГОТОВО tests/integration/test_kml_roundtrip.py:24-25 (altitudeMode = relativeToGround), :33 (тройки lon,lat,alt), :40-53 (round-trip); tests/integration/test_geojson_roundtrip.py:79-96. Оба зелёные в прогоне uv run pytest -q (150 passed) 02-system-trace.md

Архитектурные принципы A-01…A-15 (50-stack/05)

ID Требование Статус Доказательство Что доделать Отчёт
A-09 Идемпотентность мутаций по Idempotency-Key (реестр {scope,key} → полный ответ) НЕТ grep -rni "Idempotency" src/backend/gateway/src src/backend/shared/src → 0 совпадений; каталога shared/src/idempotency/ и canonical-json.ts нет (find shared -type f) GW-10: реестр ключей в БД, begin/complete, in_flight, 409 IDEMPOTENCY_KEY_CONFLICT, заголовок Idempotency-Replayed: true 06-gateway.md
A-10 Своя схема данных домена, состояние в Postgres НЕТ grep -rn '"pg"|DATABASE_URL|node-pg-migrate' gateway shared → пусто; каталога gateway/src/db/ и файлов *.repository.ts нет GW-03: пул pg, схема gateway, репозитории scenarios/plans/plan_jobs/idempotency_keys, миграции 06-gateway.md
A-01 Единственная публичная точка входа: REST /api, OpenAPI /docs, поток прогресса, файлы экспорта ЧАСТИЧНО gateway — единственный HTTP-процесс (find src/backend -name 'Dockerfile*' → только gateway/Dockerfile; grep healthz|FastAPI по planner/validator → 0); /docs и /docs-json поднимаются в main.ts:32-40; Ingress ведёт на один backend geoscan:80 (k3s/geoscan/60-ingress.yaml:18-23) Нет потока прогресса и файлов экспорта; статика фронтенда не раздаётся (STATIC_ROOT объявлен в config.service.ts:32-34, не используется); в k3s/geoscan/40-deployment.yaml:25 всё ещё 06-gateway.md
A-02 Ops-контракт: /healthz, /readyz, /status, /metrics; бизнес-логики по HTTP в доменах нет ЧАСТИЧНО Четыре маршрута — ops.controller.ts:18,24,31,42; /status собирается одной функцией buildStatusBody (shared/src/ops-http-contract.ts:10-25); Content-Type метрик 'text/plain; version=0.0.4; charset=utf-8' (ops.controller.ts:54) /readyz отдаёт {ready:true} вместо {"status":"ok"} (ops-http-contract.ts:36-38, SR-OBS-01) и всегда 200 (ops.controller.ts:27 — «always ready»); readiness.service.ts нет; барьера «в доменах нет HTTP-контроллеров» нет 06-gateway.md
A-04 Авторизация в два слоя: сервисный ключ и метаданные в gRPC, домены не парсят JWT ЧАСТИЧНО buildMetadata() по форме x-request-id/x-service-api-key/x-user-id — shared/src/grpc/metadata.ts:3-14; SERVICE_API_KEY обязателен (config.schema.ts:5) Ни одного вызова buildMetadata из гейтвея (grep → только shared/src/index.ts:8); gRPC-клиентов нет, ключ никуда не уходит; guard проверки ключа на стороне доменов отсутствует 06-gateway.md
A-05 Сквозной x-request-id: приём/генерация → metadata каждого gRPC-вызова → каждая строка лога ЧАСТИЧНО request-id.interceptor.ts:20-27 принимает или генерирует id и возвращает его в ответе; интерцептор зарегистрирован глобально в main.ts:30 Нет AsyncLocalStorage, нет передачи в planner (доменных вызовов нет вовсе), нет контекстного логгера — журнал это console.log/console.error (main.ts:47, domain-exception.filter.ts:59); shared/src/logging/logger.ts отсутствует 06-gateway.md
A-06 Единый контракт ошибок {errorCode,message,details?,errors?}, таблица gRPC→HTTP в одном месте ЧАСТИЧНО Единственная таблица — shared/src/grpc/grpc-to-http.ts:4-33 (шесть кодов A-06 + ALREADY_EXISTS→409); реестр из 18 кодов — shared/src/errors/codes.ts:2-21; единственный фильтр — domain-exception.filter.ts:16-63, подключён в main.ts:29 Маппинг мёртв: RpcServiceError нигде не бросается (доменных вызовов нет); errors[] не заполняется; 404 неизвестного пути отдаётся как errorCode INTERNAL (domain-exception.filter.ts:39-45); в одном файле два представления таблицы (const + switch) 06-gateway.md
A-07 Долгий расчёт — задача со статусом: 202 → поллинг → SSE → экспорт ЧАСТИЧНО POST /api/plans отвечает 202 (plans.controller.ts:17), GET /api/plans/:id — 200 (:39); тела — {planId, status:'queued', message:'…в GW-07'} (:31-35, :47-51) Тела не по канону (нет Location, scenarioId, queuePosition, createdAt); planId — случайный UUID, нигде не сохраняется; GET отдаёт queued для любого id, 404 PLAN_NOT_FOUND нет; нет синхронного режима time_limit_s ≤ 5, очереди, диспетчера и SSE — расчёт не запускается никогда 06-gateway.md
A-11 Конфигурация только из env через ConfigService, секреты — из creds-store, полный .env.example ЧАСТИЧНО zod-схема + fail-fast: config.schema.ts:3-31 (safeParse, печать проблем, process.exit(1)); process.env читается только в config/ (grep -rn 'process.env' gateway/src → config.schema.ts:20); паритет ключей кода и .env.example проверен скриптом → equal: True (11 ключей) Dockerfile:27 — ENV SERVICE_API_KEY=dev-local-key: ключ в git и отключённый fail-fast в образе; теста test_env_example (SR-CFG-02) нет 06-gateway.md
A-12 Наблюдаемость с первого коммита: geoscan_plan_duration_seconds, geoscan_plan_total, geoscan_validator_violations_total, gRPC-метрики ЧАСТИЧНО /metrics есть и отдаёт корректный Content-Type — ops.controller.ts:42-56 В выдаче только geoscan_process_up 1 и константа geoscan_http_requests_total 0 (ops.controller.ts:50); prom-client не подключён (gateway/package.json:11-28); metrics.interceptor.ts не создан; scrape-target и дашборды в k3s не заведены (ls k3s → geoscan, template) 06-gateway.md
A-13 Инварианты держатся барьерными тестами, а не договорённостями ЧАСТИЧНО cd src/backend && uv run pytest tests -q → 12 passed; pin-тест опций загрузчика — tests/integration/test_loader_options.py:28-47; правило «валидатор не видит планировщик» — .importlinter (контракт validator-is-independent) Pin-тест сверяет строки в package.json и loader-options.ts, а не обходит AST регистраций клиентов (SR-TST-02); нет барьера «в доменах нет HTTP-контроллеров» (A-02), снимка маршрутов Fastify (SR-API-01), deny-списка зависимостей и dependency-cruiser (SR-SVC-02); в gateway нет ни одного TS-теста и тест-раннера 06-gateway.md
A-14 Новый сервис — из шаблона, по чеклисту (пп. 1–5) ЧАСТИЧНО Каркас по шаблону: main.ts, app.module.ts, config/, ops-модуль, gateway/Dockerfile П.2 (проверка сервисного ключа) и п.4 (регистрация клиента домена + BFF-контроллер) не сделаны; п.5: docker-compose.yml отсутствует (find . -name 'docker-compose*' → пусто), в k3s/geoscan/ шаблонные /, Prometheus не тронут 06-gateway.md
A-08 Транзакционный outbox и единая топология шины — только если заводим шину ВНЕ ОБЪЁМА grep -rn 'amqp|rabbit' gateway shared → пусто; принцип условный, 50-stack/05-service-architecture.md:150-152 («RabbitMQ, скорее всего, избыточен») 06-gateway.md
A-03 .proto — источник правды, опции загрузчика зафиксированы одной функцией (keepCase, longs, arrays) ГОТОВО shared/src/grpc/loader-options.ts:4-13 (keepCase: true, longs: Number, enums: String, defaults: true, oneofs: true, arrays: true); единственный protoLoader.loadSync — shared/src/grpc/client-factory.ts:17-21; те же флаги в gateway/package.json:6; pin-тест зелёный: uv run pytest tests -q → 12 passed (tests/integration/test_loader_options.py); поля proto в snake_case (proto/geoscan/planner.proto:20-22) 06-gateway.md
A-15 Соразмерность: минимум процессов, целевой контур — четыре ГОТОВО find src/backend -name 'Dockerfile*' → только gateway/Dockerfile; planner и validator существуют как библиотеки и CLI; лишних процессов не заведено 06-gateway.md

Кодовая база и аспекты реализации (92-code и пр.)

ID Требование Статус Доказательство Что доделать Отчёт
92-code/06 §6.5 Типы фронтенда генерируются из OpenAPI (api-types.ts), руками не пишутся НЕТ ls src/frontend/packages/domain/src/ — api-types.ts отсутствует; grep -rn "api:types\\|openapi-typescript" src/frontend/*/*/package.json → пусто; packages/api-client/src/endpoints/*.ts типизированы как unknown (plans.ts:12, scenarios.ts:3, validate.ts:3, fleet.ts:3) Завести скрипт api:types и генерацию из /docs-json после появления реального OpenAPI 03-contracts.md
FE-01 Каркас качества: ESLint + dependency-cruiser + Vitest НЕТ В src/frontend нет eslint.config./.eslintrc/.dependency-cruiser/vitest.config.; src/frontend/package.json:31-34 — devDependencies только concurrently и typescript, скриптов lint/test/depcruise нет FE-01 целиком 07-frontend.md
FE-02 Гигиена воркспейсов: убрать .d.ts, дописать зависимости, включить минификацию НЕТ git ls-files 'src/frontend/packages//src//.d.ts' по-прежнему даёт 24 файла (packages/domain/src/index.d.ts и соседние); apps/shell/vite.config.ts:61 — minify:false; packages/ui/package.json не объявляет echarts/zustand/@geoscan/* Закрыть D-01, D-02, D-05 из 92-code/02 07-frontend.md
FE-04 SSE-клиент: подписка, snapshot в кеш, watchdog, закрытие на терминальном событии НЕТ Файла sse.ts нет; grep -rn "EventSource" src/frontend — пусто; GET /api/plans/{id}/events не вызывается (endpoints/plans.ts содержит только createPlan/getPlan/replan) Реализовать по §5.2 архитектуры 07-frontend.md
FE-05 Слой серверного состояния на TanStack Query НЕТ @tanstack/react-query отсутствует в src/frontend/package-lock.json и во всех package.json воркспейсов; кеша и staleTime нет нигде Ввести Query-слой либо зафиксировать отказ решением 07-frontend.md
FE-07 packages/domain ужимается до предпросмотра НЕТ packages/domain/src/index.ts:6,8,9 реэкспортируют planner, validation, exporters; apps/validator/src/screens/ParetoScreen.tsx:29-30 вызывает solve дважды Вынести решатель/валидатор/экспорт за флаг dataSource 07-frontend.md
FE-11 Общий фрагмент shared Module Federation + pin-тест НЕТ Блок shared скопирован дословно: apps/shell/vite.config.ts:44-49 и аналогичные блоки в planner/validator/viewer3d; tools/mf-shared.ts не создан Вынести в общий модуль и закрыть барьерным тестом 07-frontend.md
FE-12 Контракт RemoteScreen, реестр экранов, проп navigate, deep-link /plan/:planId НЕТ packages/state/src/screen.ts не создан (ls packages/state/src → api-compute, create-mission-store, datasource, hooks, index, runtime-config); apps/shell/src/App.tsx:30-40 — восемь Route, перечисленных руками, без параметров Реестр экранов, единый тип пропов экрана, planId в URL 07-frontend.md
FE-13 verify с утверждениями, общая фикстура контрольных чисел НЕТ tools/verify-numbers.ts:28-34 — только console.log('… (ожидается 1,96)'), ни одного assert; git log --oneline -- src/frontend/tools/verify-numbers.ts → единственный коммит 48df06b Переделать в падающий тест 07-frontend.md
FE-14 Импорт геофайлов (KML/KMZ/GeoJSON/SHP) НЕТ Ручки импорта в api-client нет; apps/shell/src/screens/StartScreen.tsx по-прежнему с beforeUpload={() => false} Реализовать после решения ОВ-6 07-frontend.md
FE-R-06 Доступ к стору только через useMission/useActions НЕТ Хуки есть (packages/state/src/hooks.ts:4,44), но grep -rn "useMission|useActions" apps — ни одного вхождения; все экраны и apps/shell/src/TopBar.tsx:4 импортируют useStore из zustand напрямую; вдобавок дубль в packages/ui/src/store-hooks.ts:10,14 Перевести экраны, удалить копию в ui, включить правило линтера 07-frontend.md
FE-R-14 DTO только генерацией из OpenAPI; снимок сверяется с /docs-json НЕТ packages/api-client/openapi.snapshot.json:6 — «Минимальный снимок до появления живого /docs-json гейтвея», написан руками, 4 операции из 11 маршрутов раздела 1 (нет /api/scenarios, /api/plans/{id}, /events, /export/{fmt}, /replan); барьера сверки нет Генерация типов + барьерный тест снимка 07-frontend.md
FE-R-15 Мутации расчёта уходят только с Idempotency-Key НЕТ packages/api-client/src/endpoints/plans.ts:12-16 — заголовок добавляется только если ключ передан; вызов без ключа проходит молча; барьерного теста нет (тестов нет вообще) Сделать ключ обязательным параметром и закрыть тестом 07-frontend.md
INT-1 CLI планировщика читает сценарии репозитория НЕТ uv run python -m planner.cli run --scenario scenarios/s02-nfz.json --out /tmp/p02.json → EXIT=1, pydantic: solver.timeLimitS Field required, objective.energy_reserve Extra inputs are not permitted, landing_sites/expected Extra inputs are not permitted (CLI использует geoscan_contracts.schema.Scenario, сценарии — под geoscan_contracts.scenario.Scenario) Свести планировщик и документы к одному контракту сценария 04-planner-core.md
INT-2 План планировщика читается валидатором (метод «валидатор раньше продукта») НЕТ load_plan(Path('/tmp/plan_s01.json')) → InvalidDocument: поле «scenario_id» — Field required; после ручного перевода в snake_case и починки lambda_ — 70 ошибок (phases.0.path Extra inputs are not permitted, phases.0.photos Input should be a valid integer) Единый контракт плана для planner и validator (SR-RPC-02) 04-planner-core.md
INT-3 gRPC-сервис Planner (proto/geoscan/planner.proto:5-7, 92-code/05 §2 rpc/) НЕТ grep -rn "grpc|serve" src/backend/planner/ → ни одного совпадения; каталога planner/rpc/ нет Сервер Plan (server-streaming) + ExportPlan + ops-HTTP :3001 04-planner-core.md
INT-4 Тесты на сквозной расчёт pipeline.plan() НЕТ grep -rn "pipeline" planner/tests tests → пусто; есть только test_geometry_optics.py (5) и test_kml_export.py (1); uv run pytest planner/tests tests/integration -q → 18 passed in 0.04s Golden-тесты на s01…s10 (70-plan/02) и барьерные тесты на NFZ, бюджет, ветер 04-planner-core.md
LINT Контракты import-linter для слоёв planner (92-code/05 §2.1) НЕТ .importlinter содержит только контракты validator/geoscan_contracts, planner как source_modules отсутствует; uv run lint-imports → error: Failed to spawn: lint-imports Добавить контракты planner-layers и export-isolation, поставить import-linter в dev-зависимости 04-planner-core.md
Makefile: lint / test / format-check Цели проверок качества в сборочном файле НЕТ src/backend/Makefile прочитан целиком (13 строк): ровно три цели — proto-py, proto-ts, proto. Целей lint, test, format-check, typecheck, build нет Добавить цели-обёртки над ruff, mypy, pytest, lint-imports 10-build-ops.md
Makefile: proto-py make proto-py воспроизводимо генерирует Python-стабы НЕТ make -n proto-py → 'make: command not found' (make в среде нет); ручной прогон той же команды: uv run python -m grpc_tools.protoc -I proto … → 'ModuleNotFoundError: No module named grpc_tools', EXIT=1; grep -rn 'grpcio-tools' --include=pyproject.toml src/backend → пусто; grep -n 'grpcio' src/backend/uv.lock → пусто; command -v python → exit 1 (есть только python3), а Makefile:5 зовёт python Объявить grpcio-tools dev-зависимостью workspace'а, включить proto-gen в members, вызывать python3 10-build-ops.md
SEC-R-03 Заголовки безопасности одним модулем (@fastify/helmet), CSP НЕТ grep -rn 'helmet' src/backend --include='*.ts' → NONE; в src/backend/gateway/package.json:11-28 зависимости нет; CSP-меты нет в src/frontend/apps/shell/index.html Подключить helmet, задать CSP (SEC-R-56…60) 09-security-config.md
SEC-R-13 internal: true в compose и NetworkPolicy в k3s НЕТ docker-compose.yml отсутствует; ls k3s/geoscan/ → 00-namespace, 10-certificate, 40-deployment, 50-service, 60-ingress — NetworkPolicy нет Добавить обе декларации 09-security-config.md
SEC-R-15 Непривилегированный пользователь, readOnlyRootFilesystem, drop ALL НЕТ src/backend/gateway/Dockerfile — директивы USER нет (рантайм node:22-bookworm-slim под root); k3s/geoscan/40-deployment.yaml:23-42 — securityContext отсутствует Добавить USER в образ и securityContext в манифест 09-security-config.md
SEC-R-19 Лимит частоты @fastify/rate-limit на всех /api/* НЕТ grep -rn 'rate-limit' src/backend --include='*.ts' → NONE Подключить вместе с контрактом ошибок 09-security-config.md
SEC-R-24 Один глобальный AuthGuard (APP_GUARD) как точка врезки НЕТ grep -rn 'APP_GUARD|AuthGuard' src/backend --include='*.ts' → NONE; src/backend/gateway/src/app.module.ts guard не регистрирует Завести заглушку guard с комментарием D-01 09-security-config.md
SEC-R-43 Редакция секретов в журнале по путям (pino redact / structlog) НЕТ Логгер не настроен: src/backend/gateway/src/main.ts:24 bufferLogs, далее console.log/console.error (main.ts:47,52; config.schema.ts:28); LOG_LEVEL нигде не потребляется — grep 'logLevel' даёт только config.service.ts:8-10 Подключить pino с redact, пробросить LOG_LEVEL 09-security-config.md
SEC-R-48 Новый секрет сразу в creds-store + .env.example + sync-secrets.sh + таблица §3 НЕТ list_keys("project/geoscan") → только CLAUDE_CODE_OAUTH_TOKEN_NUBBLE; SERVICE_API_KEY и POSTGRES_PASSWORD отсутствуют; scripts/sync-secrets.sh не существует Завести оба ключа с комментариями и написать скрипт 09-security-config.md
SMEAR Ограничения V_g ≤ Bx/Δt_min и смаз V·t_exp ≤ GSD НЕТ grep -rn "smear|min_trigger|t_exp" src/backend/planner/ → пусто В 30-domain/01:95-114 помечено «опционально» — отложить осознанно, записав в известные ограничения 04-planner-core.md
TR-11 Запрет импорта planner распространён на validator/tests/** НЕТ Копия дерева /tmp/blint с import planner в validator/tests/fixtures.py → lint-imports --config .importlinter = Contracts: 4 kept, 0 broken; uv run pytest /tmp/bypass/tests/test_independence.py -q → 56 passed. Причина: validator/tests/test_independence.py:13 сканирует parents[1]/"validator" (только исходники) Включить validator.tests в root_packages/контракт либо расширить PACKAGE_ROOT AST-скана 05-validator.md
planner → validator Сквозной путь: выход планировщика читается валидатором (метод «валидатор раньше продукта») НЕТ Ни одного такого теста: validator/tests/fixtures.py:1-6 прямо пишет «Планы для тестов собираются здесь, а не берутся у планировщика». Путь сломан: uv run python -m planner.cli run --scenario scenarios/s01-simple.json → 19 validation errors for Scenario; дамп плана валидатора не читается моделью планировщика → 262 validation errors for Plan Свести представления сценария и плана, завести сквозной тест pipeline → validate 08-tests.md
§6.1 Адреса remote'ов по умолчанию относительные (/remotes//remoteEntry.js) НЕТ apps/shell/vite.config.ts:28,33,38 — дефолты http://localhost:5174/5175/5176; ключа remotes в AppRuntimeConfig нет (packages/state/src/runtime-config.ts:3-6) Сменить дефолт на относительный, добавить переопределение из config.json 07-frontend.md
§6.2-dev Dev-прокси /api → http://localhost:3000 в конфиге shell НЕТ apps/shell/vite.config.ts:62 — server: { port: 5173, strictPort: true, fs: {...} }, секции proxy нет Добавить прокси, чтобы dev и прод отличались только config.json 07-frontend.md
В1 Экраны и потоки переключены на api-client НЕТ grep -rn "@geoscan/api-client" src/frontend → только packages/state/src/{api-compute,create-mission-store}.ts и конфиги apps/shell. Ни одного импорта в apps/planner/, apps/validator/, apps/viewer3d/, apps/shell/src/screens/. Экраны на моках: apps/shell/src/screens/StartScreen.tsx:13 (SCENARIO_CARDS), apps/validator/src/screens/ParetoScreen.tsx:6 (solve), apps/planner/src/screens/ExportScreen.tsx:16, apps/planner/src/screens/PlanScreen.tsx:6 (UAV_MODELS) Перевести стартовый экран на GET /api/scenarios, Парето/бейзлайн — на ответ плана, экспорт — на GET /api/plans/{id}/export/{fmt}, справочники — на GET /api/fleet 07-frontend.md
В2 Типы api-client согласованы с контрактом гейтвея и src/backend/schema/*.json НЕТ Типов нет: packages/api-client/src/endpoints/fleet.ts:3,7, plans.ts:11,19,28, scenarios.ts:3,7,11, validate.ts:3 — везде Promise; src/generated/schema.ts отсутствует. Прикладные типы — camelCase из domain (types.ts:83 makespanS, :112 windSpeedMs) против snake_case схемы (plan.schema.json makespan_s, scenario.schema.json wind_speed_ms) Сгенерировать schema.ts из /docs-json, написать мапперы snake↔camel, закрыть барьером FE-R-14 07-frontend.md
Воспроизводимость npm-сборки Лок-файлы для gateway и shared, npm ci вместо npm install НЕТ find src/backend -name 'package-lock.json' -o -name 'npm-shrinkwrap.json' -o -name 'yarn.lock' -o -name 'pnpm-lock.yaml' → пусто; src/backend/gateway/Dockerfile:6,14,16 используют npm install Закоммитить package-lock.json для gateway и shared, перейти на npm ci 10-build-ops.md
Покрытие ядра planner Оптимизатор, нарезка галсов, проекция, бейзлайн покрыты тестами НЕТ uv run --with pytest-cov pytest --cov=planner: pipeline.py 215 стейтментов 0%, coverage/transects.py 68 — 0%, geometry/projection.py 79 — 0%, geometry/shapes.py 29 — 0%, baseline/equal_split.py 63 — 0%, coverage/swath.py 17 — 0%. На planner приходится 6 тестов из 150 Тесты на нарезку галсов, проекцию планировщика, распределение по бортам и наивный бейзлайн 08-tests.md
Сквозной прогон planner→validator Валидатор проверяет план, произведённый планировщиком НЕТ uv run python -m planner.cli run --scenario scenarios/s01-simple.json --objective makespan --out …19 validation errors for Scenario (tasks.0.gsdCm Field required, quality/spare_batteries/available_from extra_forbidden); дамп geoscan_contracts.schema.Plan не читается load_plan: «поле «scenario_id» — Field required» (by_alias) и «solver.lambda_ — Extra inputs are not permitted» (snake). Схемы: shared-py/geoscan_contracts/schema.py:176 (Phase.path) против shared-py/geoscan_contracts/plan.py:83 (Phase.geometry) Свести планировщик и валидатор на одну схему плана либо написать конвертер с round-trip-тестом 05-validator.md
Схема плана ↔ geoscan_contracts.plan Канонический вид документа плана НЕТ Прогон: Plan(...).model_dump_json(by_alias=True) против schema/plan.schema.json → '/solver -> Additional properties are not allowed (lambda, stop_reason)' и '/metrics/violations/nfz -> 12.5 is not of type integer'. Источник: plan.py:33 (алиас lambda против lambda_frac в plan.schema.json:72), plan.py:39 (stop_reason), plan.py:42-51 (ViolationCounts — float) против plan.schema.json:92-99 (integer) Согласовать тип violations (величины number против счётчиков integer), внести stop_reason, выровнять lambda_frac 03-contracts.md
Схема сценария ↔ 40-formats/03 и фикстуры Единый документ входа для схемы, фикстур s01–s03 и pydantic-модели НЕТ uv run --with jsonschema …: s01-simple.json errors: 5, s02-nfz.json: 5, s03-multi-sites.json: 7. Конкретно: '/' → 'expected' was unexpected; /launch_sites/0 → 'turnaround_time_s' is a required property + 'turnaround_time_min' was unexpected; /objective → 'energy_reserve_frac' is a required property + 'energy_reserve' was unexpected. Источники: schema/scenario.schema.json:96,211 против scenarios/s01-simple.json:16 и scenario.py:32,139 (те дословно следуют 40-formats/03:28,80) Выбрать одно имя (Q-CT-3 рекомендует _s/_frac), привести схему, фикстуры, scenario.py и 40-formats/03 одним PR; решить судьбу поля expected 03-contracts.md
Тесты gateway (TS) Юнит- и барьерные тесты NestJS-шлюза НЕТ src/backend/gateway/package.json: scripts = proto:gen, build, start, start:dev — цели test нет; devDependencies без тест-раннера; find -name ".spec.ts" -o -name ".test.ts" по src/ пуст Добавить тест-раннер и тесты; прогнать в среде с node 08-tests.md
Тесты фронтенда Тесты packages/api-client, packages/state, packages/domain и приложений НЕТ src/frontend/package.json — скрипта test нет, devDependencies = concurrently + typescript; в packages/*/package.json тест-скриптов нет. tools/verify-numbers.ts не тест: grep -n "assert|process.exit|throw" по файлу пуст, он печатает «ожидается 1,96» в console.log и всегда выходит с 0 Ввести vitest, превратить verify-numbers.ts в тест с ассертами 08-tests.md
FE-03 packages/api-client: типы из OpenAPI, fetch-обёртка, x-request-id, Idempotency-Key, разбор ошибок ЧАСТИЧНО Есть: client.ts:49-99 (обёртка), :55-63 (x-request-id), :11-21 (ApiError), endpoints/plans.ts:12-16 (Idempotency-Key). Нет: типов (unknown), sse.ts/mappers.ts/generated/schema.ts, разбора кодов GSD_UNREACHABLE/QUEUE_FULL/UPSTREAM_UNAVAILABLE (grep по пакету пусто), проверки размера тела, тестов против мок-сервера Типы, SSE, таблица ошибок, msw-тесты 07-frontend.md
FE-06 Рассечение стора: packages/state, draft-слайс, compute() → мутация + подписка ЧАСТИЧНО Пакет создан, compute подменяется (packages/state/src/create-mission-store.ts:23-41), но слайсов нет: packages/domain/src/store.ts:43-115 — тот же плоский стор, dirty выставляется руками (store.ts:320 и др.), store.ts:17-18 импортирует solve/validate, computing:boolean не заменён статусом задачи Разделить draft/серверное, ввести status/error/progress 07-frontend.md
FE-08 Фабрика источника данных mock|api и перевод экранов на серверные данные ЧАСТИЧНО Флаг читается ровно в одном месте: packages/state/src/datasource.ts:4, используется в create-mission-store.ts:17. Но экраны не переведены (см. В1), а api-compute.ts:52,54 ставит baseline:null и pareto:[] Перевод экранов; определить источник бейзлайна и Парето (ОВ-3) 07-frontend.md
FE-10 Runtime config.json: один бандл для локального контура и geoscan.ff ЧАСТИЧНО Загрузка до createRoot реализована: apps/shell/src/main.tsx:10-23, apps/shell/src/runtime-config.ts:11 (fetch('/config.json', {cache:'no-store'}) с фоллбэком). Нет ключей remotes и buildCommit (packages/state/src/runtime-config.ts:3-6); в поставке только apps/shell/public/config.json.example; фронт не контейнеризован (find . -name Dockerfile* → только gateway и api-client), в k3s/geoscan/ статики и ConfigMap нет Добавить remotes/buildCommit, поставку config.json, образ и манифест 07-frontend.md
FE-R-05 fetch/EventSource только внутри api-client ЧАСТИЧНО Единственный fetch вне пакета — apps/shell/src/runtime-config.ts:11 (загрузка config.json), что предписано §6.2; но правило ничем не удерживается — линтера нет (FE-01) Включить no-restricted-globals после FE-01 07-frontend.md
LAYOUT Раскладка пакета planner по 92-code/05 §2 ЧАСТИЧНО Есть geometry/{optics,wind,projection,shapes}, coverage/{transects,swath}, export/{kml,geojson,common}, baseline/equal_split, pipeline, cli. Нет geometry/kinematics.py, coverage/{airspace_clip,decompose,angle,turns}.py, всего routing/, export/{qgc_plan,waypoints}.py, rpc/, tests/{unit,golden,barriers}, .env.example; логика угла и разворотов вшита в pipeline.py (517 строк) Разнести по спецификации либо обновить спецификацию 04-planner-core.md
PY-01 ruff, mypy --strict, lint-imports зелёные из чистого клона ЧАСТИЧНО uv run ruff check .error: Failed to spawn: ruff; в src/backend/.venv/bin нет ruff/mypy/lint-imports; dev-групп с ними нет ни в src/backend/pyproject.toml, ни в validator/pyproject.toml:18. Конфиги [tool.ruff]/[tool.mypy] есть (src/backend/pyproject.toml:14,32) Завести dev-группу с ruff, mypy, import-linter, hypothesis 05-validator.md
SEC-R-41 Секрет читается только слоем конфигурации, приватным полем / SecretStr ЧАСТИЧНО TS: src/backend/gateway/src/config/config.service.ts:6,12-14 — приватное поле env и геттер serviceApiKey, прямых process.env.SERVICE_API_KEY нет. Python-слоя (Settings/SecretStr) не существует; барьерного теста нет Слой конфигурации в Python + барьерный тест 09-security-config.md
SEC-R-42 Секрет не сериализуется, toJSON()/repr печатает *** ЧАСТИЧНО В /status секрета нет — src/backend/gateway/src/ops/ops.controller.ts:31-40 отдаёт имя/версию/commit/boot. Но у ConfigService нет toJSON(): JSON.stringify(config) распечатает поле env целиком (config.service.ts:6); теста нет Добавить toJSON() с маскированием и тест 09-security-config.md
SEC-R-45 .env.example полон, секреты с префиксом dev-, реальных значений нет ЧАСТИЧНО Префикс соблюдён (src/backend/gateway/.env.example:2), реальных значений нет. Полноты нет (см. SR-CFG-02); scripts/check-secrets.sh отсутствует Дополнить .env.example по реестру, написать check-secrets.sh 09-security-config.md
SEC-R-46 Dockerfile: секретов через ARG/ENV нет, .dockerignore, порядок слоёв ЧАСТИЧНО Multi-stage и порядок для gateway соблюдены (src/backend/gateway/Dockerfile:12-16). Нарушено: Dockerfile:27 ENV SERVICE_API_KEY=dev-local-key в runtime-слое; .dockerignore в репозитории нет (find . -name '.dockerignore' — пусто); Dockerfile:5 COPY src/backend/shared/ . каталогом целиком; src/frontend/packages/api-client/Dockerfile:8-10 — npm install после копирования исходников Снять ENV, добавить .dockerignore, сузить COPY 09-security-config.md
SEC-R-47 Секретов в git нет; сканер по дереву и истории в CI; check-secrets.sh ЧАСТИЧНО Факт подтверждён прогонами grep по дереву и по git log -p f420004~1..422e43a (0 находок кроме dev-local-key). Автоматики нет: .gitea/, .github/, .gitlab-ci.yml отсутствуют; scripts/check-secrets.sh нет Завести gitleaks-job и scripts/check-secrets.sh 09-security-config.md
SWATH-LIDAR swath_spacing отдельной формулой для LiDAR (2·H·tan(FOV/2)) и геофизики ЧАСТИЧНО Геофизика обработана (optics.py:94-95, line_spacing_m), LiDAR падает в общую кадровую формулу (optics.py:96-97) вопреки 30-domain/02:70; практического вреда сейчас нет — LiDAR-нагрузок в каталоге нет (payloads.yaml:227-229) Ветка SurveyType.LIDAR + поле fov_deg в каталоге нагрузок 04-planner-core.md
В3 packages/state: что хранит и подключён ли к приложению ЧАСТИЧНО Подключён: apps/shell/src/App.tsx:3,21 — createMissionStore(runtimeConfig) в useMemo, стор пропом в TopBar и RemoteView. Содержит фабрику с подменой compute/replanWithout/restoreFleet (create-mission-store.ts), runtime-config.ts, datasource.ts, hooks.ts, api-compute.ts:12 («Заглушка расчёта через гейтвей»). Собственного состояния не хранит — стор по-прежнему packages/domain/src/store.ts; tsconfig.json у воркспейса отсутствует (ls packages/state) Перенести стор и слайсы в пакет по §3.2, добавить tsconfig 07-frontend.md
Детерминизм сравнения планов normalize_plan с явным списком изменчивых полей ЧАСТИЧНО shared-py/geoscan_contracts/normalize.py:13-19 (generated_at, plan_id, request_id, solver.solve_time_s, generator), :47-54 — канонические байты, sort_keys, round(x, 6). Критик: normalize.py:13-19 перечисляет поля в snake_case, планировщик пишет camelCase (by_alias=True) — normalize_plan на реальных планах не вычищает generatedAt, прогон дал equal: False Синхронизировать список изменчивых полей с фактической сериализацией планировщика 03-contracts.md
Инструменты качества (ruff/mypy/import-linter) Конфиги линтеров и архитектурных контрактов исполнимы ЧАСТИЧНО Конфиги есть: src/backend/pyproject.toml:11-40 ([tool.ruff] 14 наборов правил, [tool.mypy] strict=true), src/backend/.importlinter (4 контракта). Но uv run ruff --version / mypy --version / lint-imports --help → 'error: Failed to spawn: … No such file or directory'; grep по всем pyproject.toml не находит ruff/mypy/import-linter ни в dependencies, ни в dependency-groups; корневой pyproject.toml вовсе не имеет [project]/[dependency-groups] Добавить dev-группу с ruff, mypy, import-linter; перегенерировать uv.lock; завести make-цели 10-build-ops.md
Сверка declared vs recomputed План, который врёт о себе, не проходит валидацию ЧАСТИЧНО declared_metrics прокинуты в отчёт (validator/validator/validate.py:75), но сверяются только coverage_frac (checks/g6_coverage.py:52) и turn_time_frac (:132); makespan_s, total_flight_time_s, transects_total, photos_total, uavs_used, sorties_total не сверяются Добавить проверку расхождения объявленных и пересчитанных метрик по всем полям 05-validator.md
Чистота пересчёта покрытия Метрики считаются только по геометрии плана, без данных решателя ЧАСТИЧНО validator/validator/metrics.py:44-57 — при недоступности своей оптики ширина полосы берётся из плана (footprint_across_m/swath_spacing_m); путь помечается в Coverage.fallbacks и выводится в objects G6-01, но статус остаётся pass (checks/g6_coverage.py:74) Понизить статус до warn при непустом fallbacks либо считать покрытие неизвестным 05-validator.md
BUILD Сборка и запуск гейтвея (npm run build, docker build, живые ответы ручек) НЕ ПРОВЕРЕНО which node npm → пусто; src/backend/gateway/node_modules не существует; по условиям ревью установка Node/npm запрещена Прогнать npm ci && npm run build и smoke по четырём ops-маршрутам и /docs-json на машине с Node 06-gateway.md
CN Контрольные числа 30-domain (GSD 1,96 см; 117×78 м; By 47,0 м; H 153 м) НЕ ПРОВЕРЕНО Прогон невозможен: Node/npm в среде нет (which node npm npx → пусто, node -v → command not found), npm run verify не запускается. Косвенно: git log --oneline -- src/frontend/tools/verify-numbers.ts src/frontend/packages/domain/src/coverage.ts → единственный коммит 48df06b, в составе f15e917 этих файлов нет Прогнать после появления Node; данное изменение чисел не трогает 07-frontend.md
E2E Поведение dataSource: "api" на живом контуре НЕ ПРОВЕРЕНО Ни фронт, ни гейтвей поднять нельзя (нет Node); гейтвей в текущем виде — заглушки (src/backend/gateway/src/plans/plans.controller.ts:31-35,47-51) Прогон e2e после GW-07/GW-12 07-frontend.md
SEC-R-49…55 Безопасность выгрузки (Content-Disposition, MIME, zip) НЕ ПРОВЕРЕНО Ручек выгрузки в gateway нет; экспортеры лежат в src/backend/planner/planner/export/ и src/frontend/packages/domain/src/exporters.ts — это зона профильного ревью экспорта, не конфигурации Проверить в ревью экспорта/фронтенда 09-security-config.md
SEC-R-56…60 CSP фронтенда НЕ ПРОВЕРЕНО Статически: CSP нигде не задаётся (helmet нет, в src/frontend/apps/*/index.html нет). Эмпирический прогон невозможен — в среде нет Node.js/npm Задать CSP и проверить прогоном демо в браузере 09-security-config.md
TC npm run typecheck на новых пакетах НЕ ПРОВЕРЕНО Node/npm отсутствуют. Статически ошибок не видно: реэкспортируемые из @geoscan/domain типы существуют (store.ts:29,31,115; types.ts:271) Прогнать npm run typecheck 07-frontend.md
buf lint Линт proto-контрактов НЕ ПРОВЕРЕНО command -v buf → не установлен; цели в Makefile нет, только упоминание в src/backend/README.md ('buf lint — при установленном buf') Добавить цель proto-lint и buf в тулчейн CI 10-build-ops.md
buf lint (критерий SR-RPC-01, SR-RPC-07) buf lint и protoc проходят по всему каталогу proto НЕ ПРОВЕРЕНО buf/protoc не установлены — прогон невозможен. Статически при lint.use: [DEFAULT] (proto/buf.yaml:4-8, исключены только PACKAGE_SAME_GO_PACKAGE и FILE_LOWER_SNAKE_CASE) видны кандидаты на падение: PACKAGE_DIRECTORY_MATCH (geoscan/common.proto при пакете geoscan.common.v1; о проблеме предупреждает src/backend/README.md:29), SERVICE_SUFFIX (сервисы Planner/Validator без суффикса Service — planner.proto:5, validator.proto:5), RPC_RESPONSE_STANDARD_NAME (rpc Plan возвращает PlanEvent — planner.proto:6) Прогнать buf lint и либо добавить исключения в buf.yaml, либо переразложить каталог/переименовать сервисы 03-contracts.md
make proto-ts Кодогенерация TS-типов из proto НЕ ПРОВЕРЕНО Цель Makefile:9-10 — 'cd gateway && npm run proto:gen'; command -v npm → пусто, установка npm запрещена заданием. Статически: gateway/package.json script proto:gen → 'proto-loader-gen-types … ../proto/geoscan/*.proto', путь из контейнера (/build/gateway/../proto) совпадает с Dockerfile:10 Проверить на машине с npm или в CI 10-build-ops.md
proto-gen ↔ proto Сгенерированный код не разъехался с исходными .proto НЕ ПРОВЕРЕНО Перегенерация невозможна: нет buf/protoc/grpc_tools. Статически разъезжаться нечему — find proto-gen -type f → README.md, pyproject.toml, src/geoscan/init.py (одна строка docstring); генерация не выполнялась ни разу 03-contracts.md
Реализация TS (gateway, shared) Поведение шлюза и общих gRPC-хелперов НЕ ПРОВЕРЕНО В среде нет node/npm (ограничение задания); gateway/package.json без цели test — запускать нечего. Прочитано статически: shared/src/grpc/loader-options.ts (13 строк), metadata.ts (14), grpc-to-http.ts (46), client-factory.ts (45) Прогнать в среде с node после появления тестов 08-tests.md
Типобезопасность фронтенда npm run typecheck и npm run verify НЕ ПРОВЕРЕНО Нет node/npm. Скрипты объявлены в src/frontend/package.json (typecheck, verify), но verify запускает tools/verify-numbers.ts — печать чисел без ассертов Прогнать typecheck в среде с node; verify переделать в тест 08-tests.md
G2-04 Проверка запаса над рельефом ВНЕ ОБЪЁМА validator/validator/checks/g2_altitude.py:20 MISSING_CHECKS; в выводе validator check печатается «не реализовано: G2-04 — запас над рельефом: во входной схеме нет модели рельефа» 05-validator.md
SEC-R-30…40 Защита разбора импорта (XXE, zip-бомбы, офлайн-CRS, отравленные числа) ВНЕ ОБЪЁМА Импорта файлов в реализации нет: src/backend/gateway/src/plans/plans.controller.ts и validate.controller.ts — заглушки, модулей import/ нет; xml.etree встречается только в тесте tests/integration/test_kml_roundtrip.py:4 Проверять после появления модуля импорта 09-security-config.md
.importlinter (содержание) Контракты: независимость валидатора, ядро без транспорта, контракты без геометрии, слои валидатора ГОТОВО src/backend/.importlinter:6-60; прогон uv run --with import-linter --python 3.12 lint-imports --config .importlinter → «Analyzed 74 files, 279 dependencies. Contracts: 4 kept, 0 broken» 05-validator.md
A-DEP Границы зависимостей: api-client без React/state/ui, state без ui/tokens/apps ГОТОВО grep -rn "react|antd|@geoscan/ui|@geoscan/tokens" packages/api-client/src packages/state/src → только import { useStore } from 'zustand' в packages/state/src/hooks.ts:1, что разрешено таблицей §1.2 архитектуры 07-frontend.md
A-IVP Признак «высота выше порога 150 м» (ФП-138) ГОТОВО pipeline.py:469 above_ivp_threshold = altitude_m > 150.0; прогон s01 при 153,19 м → above150 True, совпадает с expected.warning сценария 04-planner-core.md
CAT-1 ТТХ парка и нагрузок как данные (fleet.yaml / payloads.yaml) ГОТОВО catalog.py:273-293 (lru_cache, frozen dataclass, перевод единиц в одном месте); нагрузки без оптики отсеиваются (catalog.py:341-350): payloads passing loader: ['geoscan_801_eo_wide','geoscan_801_thermal','geoscan_pf1b','pollux'] 04-planner-core.md
CN-1…CN-4 Контрольные числа 30-domain/01: GSD 1,96 см; захват 117×78 м; шаг 47,0 м; H 153 м ГОТОВО uv run python -c "…": GSD@100 cm = 1.9583; footprint@100 = 117.5 78.0; spacing q=60% = 47.0; H for GSD 3cm = 153.19409692079864; trigger@Vg=10,H=150 = 3.51. Те же числа зафиксированы тестом planner/tests/test_geometry_optics.py:47-56. Дополнительно сошлись Pollux (4.31 / 62.1×46.6 / 15.5), тепловизор 801 (13.19 / 84.4×67.5 / 21.1), PF1B на 255 и 500 м 04-planner-core.md
Round-trip контрактов Модели схем переживают сериализацию без потерь ГОТОВО tests/integration/test_geometry_contract_roundtrip.py:73/81/90 — Polygon с дыркой, MultiPolygon, 3D LineString, сверка по вершинам abs=1e-9; validator/tests/test_contracts.py:30 (сценарий, again == scenario), :36 (план побайтово), :51 (extra=forbid ловит опечатку) 08-tests.md
SEC-R-02 CORS в gateway не настраивается ГОТОВО grep -rn 'enableCors' src/backend --include='*.ts' → NONE; src/backend/gateway/src/main.ts вызова не содержит 09-security-config.md
SEC-R-05 Metadata собирается одной функцией в shared/ ГОТОВО src/backend/shared/src/grpc/metadata.ts:3-13 — единственная buildMetadata (x-service-api-key, x-request-id, опционально x-user-id); 'new Metadata(' встречается только в metadata.ts:7 09-security-config.md
SEC-R-12 В planner/validator нет HTTP-клиентов ГОТОВО planner/pyproject.toml:6-11 и validator/pyproject.toml:8-13 — только geoscan-contracts, shapely, pyproj, numpy; барьер validator/tests/test_independence.py:16; прогон: cd src/backend && uv run pytest validator/tests/test_independence.py -q → 56 passed in 0.15s 09-security-config.md
SEC-R-18 Лимит тела запроса на уровне Fastify ГОТОВО src/backend/gateway/src/main.ts:17-19 — new FastifyAdapter({ bodyLimit: env.BODY_LIMIT_BYTES }); дефолт 26214400 в config.schema.ts:9 и .env.example:6 09-security-config.md
WS-1 Воркспейсы api-client и state объявлены и залочены ГОТОВО src/frontend/package.json:7-10 — workspaces packages/*; git show 422e43a добавляет node_modules/@geoscan/api-client и @geoscan/state как link:true плюс записи packages/api-client и packages/state; apps/shell/package.json:14-15 объявляет обе зависимости; алиасы и paths — apps/shell/vite.config.ts:57-58, apps/shell/tsconfig.json:9-10 07-frontend.md
uv lock --check uv.lock консистентен с pyproject'ами workspace'а ГОТОВО cd /home/qovalenko/geoscan-wt/review/src/backend && uv lock --check → 'Resolved 25 packages in 1ms', EXIT=0 10-build-ops.md
Аспект: .gitignore Покрытие env-файлов и ключей ГОТОВО .gitignore:1-13 — .creds, .key, .pem, authorized_key.json, kubeconfig, .kube/, .env, .env. с !.env.example, -secret.yaml, sealed*.key; исключение работает — .env.example отслеживается в git, .env нет 09-security-config.md
Аспект: git-история f420004..422e43a Секреты в историю восьми коммитов не попадали ГОТОВО git log -p --diff-filter=A f420004~1..422e43a -- '.env' 'key' 'secret' '.pem' '.key' → добавлен только src/backend/gateway/.env.example; git log -p … | grep '^+.*(password|secret|token|api_key)=' → 3 строки, все dev-local-key/объявление zod-поля 09-security-config.md
Аспект: правило «секреты только в creds-store» Файлов с кредами в репозитории нет ГОТОВО Совокупно grep/find по дереву и git log -p по 8 коммитам: 0 продовых значений. Обратная сторона (секреты заведены в creds-store) не выполнена — учтено в SR-CFG-05/SEC-R-48 09-security-config.md
Аспект: секреты в рабочем дереве Реальных кредов в дереве нет ГОТОВО grep -rnEi '(BEGIN .PRIVATE KEY|ghp_|glpat-|AKIA[0-9A-Z]{16}|xox[baprs]-|sk-[A-Za-z0-9]{20,}|eyJ…)' src/ k3s/ scripts/ .json .md → 0; find по .key/*.pem/.env/kubeconfig/authorized_key.json → только имя файла docs/task5/91-system/08-config-and-secrets.md; в lock-файлах нет _auth/authToken, resolved только registry.npmjs.org 09-security-config.md
В4 Runtime config shell: адрес API задаётся извне, захардкоженных URL нет ГОТОВО grep -rn "localhost|http://|https://|.ff" apps packages tools не даёт ни одного адреса API (остаются дефолты remoteEntry в apps/shell/vite.config.ts:28,33,38, порты в комментариях standalone.tsx и namespace KML). Цепочка: config.json → normalizeRuntimeConfig (packages/state/src/runtime-config.ts:13-19) → GeoscanApiClient({baseUrl}) (create-mission-store.ts:21) → resolveApiBaseUrl (packages/api-client/src/config.ts:20, дефолт '/api') 07-frontend.md
Высота 153 м Высота под GSD 3 см = 153 м и порог ИВП 150 м ГОТОВО planner/tests/test_geometry_optics.py:56-58 — altitude_for_gsd_m(PF1B, 3.0) = 153,2±1,0; validator/tests/test_checks.py:32-46 сверяет plan.geometry[0].altitude_m с expected из scenarios/s01-simple.json, test_checks.py:49-56 — 153 м это WARN (ИВП), а не нарушение (30-domain/06-airspace-ru.md:53) 08-tests.md
Коды возврата CLI 0 / 2 / 3 / 4 по 92-code/05 §10.1 ГОТОВО validator/validator/cli/exit_codes.py:7-13; прогоны: чистый план EXIT=0; битый сценарий → «невалидный вход: /tmp/gsplans/bad.json: поле «scenario_id» — Field required», EXIT=2; инъекция БПЗ → EXIT=4 05-validator.md
Конвенция ветра wind_direction_deg — «откуда дует», одно место конверсии на бэкенд ГОТОВО shared-py/geoscan_contracts/units.py:92-97 — wind_from_to_wind_to_deg со ссылкой на 40-formats/03:91-93; scenario.py:128-129; schema/scenario.schema.json:196-197 (диапазон 0…360) 03-contracts.md
Контрольные числа 30-domain Оптика валидатора воспроизводит контрольную таблицу 30-domain/01 ГОТОВО uv run python … validator.geo.optics для PF1B при GSD 3 см → altitude_m=153.191, across=180.00, spacing@60=72.00; совпадает с scenarios/s01-simple.json (expected 153.2 / 180.0 / 72.0) и с docs/task5/30-domain/01-survey-geometry.md:151-158; uv run pytest validator/tests/test_optics.py -q внутри 132 passed 05-validator.md
Контрольные числа 30-domain/01:153 GSD 1,96 см, кадр 117 × 78 м, шаг 47,0 м при q=60 %, H=100 м ГОТОВО validator/tests/test_optics.py:36-56 — GSD 1,96/2,94/3,92/5,00/9,79 см, footprint 117,0 × 78,0 м, шаг 47,0/70,5/94,0/120,0/235,0 м, допуск 0,5 %; planner/tests/test_geometry_optics.py:45-53 — GSD 1,96±0,02, footprint 117,5±1,0 × 78,0±1,0, шаг 47,0±0,5, интервал съёмки 3,5±0,2 с 08-tests.md
Контрольные числа Pollux и тепловизора Таблицы 30-domain/01:167-186 ГОТОВО validator/tests/test_optics.py:75-129 — Pollux 4,31/6,47/8,63 см, footprint 62,1 × 46,6, шаг 15,5/23,3/31,1; тепловизор 6,59/13,19/19,78 см, footprint 84,4 × 67,5, шаг 21,1; соотношения 2,2× и 6,6× — :96-112; коэффициенты сверены с payloads.yaml — :135-144 08-tests.md
Метрики 40-formats/03 Полнота пересчитываемых метрик и счётчиков нарушений ГОТОВО validator/validator/metrics.py:147-165 recompute — makespan_s, total_flight_time_s, coverage_frac, transects_total, turns_total, turn_time_frac, photos_total, uavs_used, sorties_total, total_distance_m, violations; ViolationCounts расширены до 7 видов (shared-py/geoscan_contracts/plan.py:45-51); исторические имена coverage_fraction/turn_time_fraction приняты алиасами (plan.py:68-75); solve_time_s берётся из плана осознанно (validate.py:36) 05-validator.md
Отказы оптики и ветра Недостижимые режимы — явная ошибка, а не молчаливое число ГОТОВО validator/tests/test_optics.py:147-163 (OpticsUnavailable для riebo_r4 без sensor_width_mm и для LiDAR), :187-189 (|W⊥| ≥ V_a → None); planner/tests/test_geometry_optics.py:61-70 (Infeasible с reason_code ALTITUDE_ABOVE_MAX и CROSSWIND_EXCEEDS_AIRSPEED) 08-tests.md
Перечисления контракта (Python) Семь ViolationKind, четыре ExportFormat, UnassignedReason; токены lower_snake_case ГОТОВО shared-py/geoscan_contracts/enums.py:73-82 — семь значений ViolationKind (nfz, airspace, endurance, wind, payload, altitude, turnaround) = рекомендация (а) открытого вопроса 2 из 91-system/04; enums.py:96-100 — четыре ExportFormat; enums.py:85-93 — UnassignedReason; schema/plan.schema.json:79-99 — те же семь ключей violations 03-contracts.md
Порядок координат и вырезы GeoJSON-подобная геометрия WGS84, «долгота, широта», coordinates[0] — внешний контур ГОТОВО shared-py/geoscan_contracts/geometry.py:14-17,53-58; тест tests/integration/test_geometry_contract_roundtrip.py:73-87 проходит в составе uv run pytest -q → 150 passed in 1.25s 03-contracts.md
Проверка плейсхолдеров в deploy.sh Раскатка падает на незаполненном шаблоне ГОТОВО scripts/deploy.sh:14-18 — grep -rq '\ \ ' "$DIR" → печать вхождений и exit 1; сейчас гарантированно сработает, плейсхолдеры на месте
Проверки валидатора G1–G6 Каждая жёсткая проверка ловит своё нарушение ГОТОВО validator/tests/test_checks.py — 16 тестов, каждый портит одну вещь: NFZ в метрах (:59), зона слоем выше полёта (:98), выход за разрешённое ВП (:136), ветер выше предела (:165), встречный ветер (:184), потеря разворотов (:196), запас энергии (:204), нехватка 540 с на АКБ (:216), несовместимая нагрузка (:225), unassigned без причины (:237), дубли transect_id (:260), завышенное покрытие (:286), потолок 500 м (:297); покрытие validator/ по --cov 82…100 % 08-tests.md
Прогон всех python-тестов uv run pytest -q зелёный ГОТОВО cd /home/qovalenko/geoscan-wt/review/src/backend && uv run pytest -q → «150 passed in 1.96s», падений нет 08-tests.md
Разбор сценариев репозитория scenarios/s01…s03 читаются контрактной моделью ГОТОВО tests/integration/test_scenarios_json.py:16-47 — 6 тестов: schema_version, критерий, suitable_for, одна NFZ в s02, две площадки и три борта в s03 08-tests.md
Согласованность workspace'ов фронтенда package.json ↔ package-lock.json по всем workspace'ам ГОТОВО Скрипт сверки по корню и 9 workspace'ам (packages/api-client, state, domain, tokens, ui; apps/shell, planner, validator, viewer3d) → 'mismatches: 0'; 'declared deps not present in lock tree: []'; lockfileVersion 3, 267 пакетов, все 9 слинкованы (node_modules/@geoscan/* link: True). Резолвы: vite 8.3.0, antd 6.6.4, react 19.3.0, typescript 5.9.3, @module-federation/vite 1.21.6, maplibre-gl 5.24.0, @deck.gl/core 9.4.0 10-build-ops.md
Состав проверок (Q-PY-4) 24 проверки в 6 группах со стабильными идентификаторами ГОТОВО uv run python -m validator check … печатает ровно 24 результата: G1×5, G2×4, G3×4, G4×3, G5×4, G6×4; агрегация по объектам — checks/base.py:56; нереализованное видно строкой «не реализовано: G2-04» Зафиксировать в 92-code/05 замену предусмотренной G2-04 на не описанную там G2-05 (g2_altitude.py:135) 05-validator.md
Тесты backend Прогон тестов зелёный ГОТОВО cd /home/qovalenko/geoscan-wt/review/src/backend && uv run pytest -q → '150 passed in 1.16s' 10-build-ops.md
Тесты валидатора Прогон тестов пакета и всего бэкенда ГОТОВО cd src/backend && uv run pytest validator/tests -q → 132 passed in 1.14s; uv run pytest -q → 150 passed in 1.12s 05-validator.md
Формат отчёта 70-plan/01 Одна команда, одна таблица, бейзлайн в той же таблице, колонка viol ГОТОВО validator/validator/report/table.py:139-157 (блок «БЕЙЗЛАЙН (равное деление)») и _improvement_line:114; прогон validator run печатает шапку scenario/crit/makespan/total_fl/cover/turns%/solve/viol; тесты validator/tests/test_report_and_cli.py 05-validator.md