58 KiB
Передача разработки AirDef
Это журнал фактически сделанных изменений и ограничений проверки, а не список обещанных функций.
Рабочая ветка kimi-work; issues не закрывать, публиковать комментарии с коммитом и проверками.
Движок — отдельный chexit/horizon-airdef, ветка airdef, подключён точным gitlink horizon/.
Текущее состояние: 4 ноября 2026
Последнее продолжение пяти задач: CONTINUATION-2026-11-04.md.
Движок 57f8b2853c4443f7e3ff5701600cbb262b53118f, API29. В этом файле ниже сохранён
исторический журнал предыдущих серий; старые SHA и числа тестов не заменяют новую контрольную точку.
След #91 продолжен: catalog/specs.json — единственная копия числовых строк дронов, ракет и
комплексов; python3 tools/gen_catalog.py генерирует specs_generated.rs и
unit_specs_generated.rs, --check (CI) валит расхождение. Числа не менялись; сверка с литералами
подтвердила 1.6 как фактический голос P1-SUN (недостижимый P1sun => 1.7 в старом match не в счёт);
на это добавлена Rust-регрессия. Остаток: варианты комплексов и игровая приёмка.
Продолжение #129: стадии холодного пуска и разгон (6 ноября 2026)
Ветка agent/issue-129-cold-launch, кодовый коммит 4b013fb7f30dbaa87a266b27a31911937fc523b4
(документация, измерения и кадры — следующим коммитом этой же ветки).
Игра/движок не менялись: pin 278f356aa604fa21c2d15d0081823bbe0f891df2, API30. Push не выполнялся,
issues не закрывались.
Контракт стадий. У MissileSpec появились eject и eject_drag; cold — тики физического
выброса, boost — тики departure. guided(age) = age >= cold + boost; основная ветка наведения,
самонаведение и облёт рельефа больше не могут включиться до зажигания (раньше было age >= boost,
что при большом cold включало наведение до мотора). Зажигание — по-прежнему age > cold;
событие MotorIgnition, звук, факел и голос используют текущую позицию ракеты. departure_ramp
растёт 0→1 за boost; темп поворота в departure — turn·(0.2 + 0.8·ramp). Тяга ракетных
профилей — accel·(0.45 + 0.55·ramp), на guidance — полная. P1-SUN (flame=0) сохраняет
полный разгон без ракетной струи.
Профили. M48n6: cold=20, eject=2.4, eject_drag=0.07 — 1,00 с; M77n6: cold=24,
eject=2.6, eject_drag=0.07 — 1,20 с; IrisT/M9m338: cold=8, eject=1.8/1.6,
eject_drag=0.1 — 0,40 с. Рельсовые профили (cold=0) получили только spool тяги; ранний
поворот на рельсовом пуске был проверен и отменён: он уводил Pac3 в землю на холмах и валил
an_interceptor_over_rolling_land_does_not_hit_the_ground. speed, accel, turn, pk,
fuse и правила столкновений не менялись.
RM48U. EJECT_TICKS 5 → 20. Выброс неосвещённый и замедляется; зажигание на EJECT_TICKS,
факел/звук/голос с этого момента; насчитанное время полёта включает выброс; дым в записи
начинается от точки зажигания, а не от горловины. Live, repeat и export читают одно и то же
состояние lit/записанное MotorIgnition.
Проверки. core release 206 + 8 сценариев, 8 ignored; адаптер 67 + 1 boundary +
5 сценариев; настоящий .hmod — 30 шагов wasm_contract.toml; gen_catalog.py --check и
gen_models.py --check без расхождений; test_gen_assets.py — 10 тестов; git diff --check чист.
Новые регрессии: стадии по порядку и отсутствие powered-эффектов до зажигания; guided не раньше
cold для всех профилей; непрерывность скорости/поворота и throttled первый powered тик; P1-SUN
без факела; записанное зажигание через cold тиков и missile_is_lit по нему; RM48U без
голоса/факела до зажигания и ровно один звук зажигания.
Баланс. Сетка #120 (6 вариантов × 8 seed, 2208 целей на сторону, baseline dc36d67):
сбито 1988 → 1939, прорывов 216 → 258, ракет 3461 → 3467; near200 318 → 311, far1500
338 → 334, hills20 334 → 319; energy_lost 0 → 0, expired 1 → 0, scrub 2 → 4.
SAMS=20 SIZE=12, 12 seed: 203 → 201 сбитых, 1 → 3 прорыва, 355 → 360 ракет. Отдельные CSV:
сетка, рейды.
Это намеренное следствие более длинного старта; скрытого повышения pk или максимальной скорости нет.
Парная съёмка. Один scratch-мир ColdLaunch129, одно сохранение, сценарий pair129,
seed 965460218386186365, один клиент/движок API30, один режиссёр
(/airdef replay export … best fps=30 size=1280x720 crf=16 sec=24), Metal/offscreen.
Сценарий: С-400 1.5 72 8.5, роли 31, один Х-22 с 900; мод до — dc36d67, после — 4b013fb.
Оба MP4: h264 1280×720 30 fps + AAC 48 кГц; до — 403 кадра/13,433 с, после — 412/13,733 с.
План «Пуск» стартует на кадре 94 в обоих; первое освещение центра (окно 100×120, порог 100)
до — кадр 101 (0,233 с после старта, cold=5 = 0,25 с), после — кадр 121 (0,900 с,
cold=20 = 1,00 с); к 124 кадру устойчиво ≈198. Это подтверждает сдвиг холодной паузы на
≈0,67 с между сборками. MP4 в docs/media/cold-launch перекодированы в CRF26 для хранения;
исходные файлы, их SHA-256, ffprobe и логика измерения — в
issue-129-pair.json. Ограничение: у агента нет ввода
изображений; проверены параметры, старт плана, яркость центра и парные кадры, но композицию и
красоту кадров человеком ещё нужно оценить. Предпусковые кадры не пиксельно идентичны (живой
прогон, движение камеры, анимация листвы); совпадают мир/сейв/сценарий/seed/метод камеры.
Отдельный very-close статичный план и live-MP4 не сняты (--offscreen без окна). Это не полная
визуальная приёмка.
Продолжение #122 (второй налёт из UI)
Точный лог Windows: stopped in UI event (fuel budget 1000000000, remaining Some(0)),
backtrace known_units ← order_raid ← launch_wave ← ui_select — другой класс, чем тик
1f28f83. Причина: order_raid пересчитывал снимок известных комплексов на каждом дроне,
а сам снимок был квадратичен по журналу и группировке. Исправление: один снимок на приказ
(+ O(журнал+группировка) через множества, price_grid_on переиспользует свой снимок);
семантика целеуказания и выбора носителей не менялась, сверка с прежним предикатом в
нативном тесте, /stats в WASM совпал побайтово. Лимиты не трогали, HD/запись не отключали,
размер волны не ограничивали.
Воспроизведение в настоящем WASM (измеренный fuel): до — второй приказ 111 дронов исчерпывает
ровно 1e9 в событии (failures=1), тот же приказ 11 дронов — 247805865; после — тот же сценарий
целиком: max command 32946177/1e9, tick 236581288/300M, ошибок 0. Диагностический гость
(trigger.patch вызывает обработчик ui_select(BTN_LAUNCH)) и точный сценарий:
issue-122-ui-fuel. Воспроизвести удалось только
с разнесённой группировкой: стек в одной точке гибнет от одной боеголовки (known=0, приказ
дёшев) — у пользователя силы дожили до второго приказа.
Закреплённые регрессии: нативные «эквивалентность снимка» и «второй умный приказ с толстым
журналом принят и запущен»; тяжёлый WASM fixture расширен двумя умными приказами через
airdef preset + airdef raid (функция кнопки ПУСК) — 4 принятых приказа, SHA fixture
обновлён; лёгкий контракт 30 шагов с preset и отказом пустой волны. Новая команда
airdef preset <n> — script-эквивалент кнопки готового состава.
Последний общий прогон: core release 195 + 8 сценариев / 7 ignored; адаптер 65 + 1 boundary + 5 сценариев; лёгкий WASM 30 шагов; тяжёлый WASM 283 шага (tick max 182413686/300M, command max 6939810/1e9, failures=0), отчёт flat-wasm-raid-budget; 6 тестов отчёта budget и build-тест прошли. Ни одно issue не закрыто; пуш не выполнялся.
Контрольная точка 3 ноября
Код предыдущих четырёх задач интегрирован до 6e2d661. Продолжение #102/обратного вывоза
опубликовано до aab3e78; последнее продолжение расследования налётов — 2932364.
Текущий движок — f95f03c08ec2916a0e5bd3b2d4a2a790e7937b47, API 27.
README описывает доступное поведение; IMPLEMENTATION-STATUS.md отделяет реализацию от приёмки.
Ни одно issue не закрыто. Участники: Codex (интеграция, #119, ревью, документация) и три
GPT-6.1 Terra (энергетика, типизированная доставка, излучение). Новые числа — только игровые.
| Issue | Опубликованные контрольные коммиты |
|---|---|
| #119 | c96676f: укрытия, read-only geometry, четыре проверки |
| #118 | c775f31, 952a10f: доставка ТЗМ, возврат снятой ракеты ПВО, реальное движение и save/restore |
| #121 | 26b258f, 0df225a, 88f9405, 9e1e0f5, b377a5d, ce76f20: излучение, UI/save/replay, наблюдения, last-known SEAD |
| #120 | 143d9b2 → e38ca0d, затем a7b8cb9: конечное горение, coast, swept impact, диагностика, звук, исправленное ветвление фаз |
| WASM | 6e2d661: проверка On/Off и запрета управления отсутствующим радаром, 24 шага |
Для обновления существующей установки сначала git switch kimi-work, затем git pull --ff-only,
python3 tools/airdef.py all, python3 tools/airdef.py play. all обновляет точный сабмодуль,
собирает игру/мод и устанавливает пакет. На Windows обычно используется python вместо python3.
Продолжение #102 и обратного вывоза RM48U
d3378b3:fork()синхронизирует URL из.gitmodulesдаже на fast path совпавшего pin. Реальная Git-регрессия воспроизводит старый upstream URL и новый коммит, отсутствующий в локальном clone. Это исправляет наблюдавшееся на Windowsupload-pack: not our ref 86b1014…; пользователь подтвердил ручное исправление черезgit submodule sync --recursiveдо публикации автоматизации.bb931ab,e9fbaba,b0085cb:ground-unloadвдали от склада резервирует одну RM48U и вызывает ТЗМ.ground-cancelотменяет ещё ожидающий вывоз/доставку; после забора груз продолжает путь на склад. Проверены физическое движение и полный save/restore до забора и после забора, без телепортации запаса.910f02c: потеря зарезервированной ракеты вместе с ячейками сбрасывает заявку на вывоз. Иначе можно было создать сохранение, которое loader сам отвергал как несовместимое состояние.- Двигатель:
f4c1131d3, затемc89c78c47опубликованы вhorizon-airdef:airdefдо обновления gitlink. API27 добавляет acoustics-status и capture-owner-status, scoped к запуску мода и поколению мира. Остановка capture проверяет владельца; смена мира снимает незапущенный запрос или посылает cancel. 910f02c: команда/airdef capabilities; дальний звуковой fallback сохраняется до подготовки акустического пути, экспорт принимает только своё подтверждённое состояние; Arming ждёт до 600 тиков.prepared— milestone хоста, не GPU fence, не результат каждой акустической трассы и не готовый MP4.4dddea2,8d5d0d1: opt-intest-budgets, отдельный ручной workflow и JSON provenance; тесты ошибок сборки, stale identity и ненулевого runtime exit. Native success не заменяет actual WASM.aab3e78: repo-local packer использует target-cache закреплённого движка;CARGO_TARGET_DIRне переопределяется для сборки самого мода и не уводит.hmodв чужое дерево.
Проверки объединённого состояния: core release 190 + 4 сценария, 7 ignored;
адаптер 60 + 1 boundary + 4 сценария; лёгкий WASM 26 шагов, тяжёлый 69 шагов;
Git-регрессия и три теста failure report прошли. Исходный профиль запрашивал 64 ПВО + 160 целей,
602 tick / 300 frame; JSON фиксирует clean aab3e78, pin API27 и совпадающий build identity.
Полные данные: budget report.
Исправление оценки покрытия: второй запрос исходного 69-шагового сценария отклонялся во время
подготовки первого. Его успешный exit не подтверждает 160 запущенных или одновременно летящих целей.
В архивный JSON добавлена поправка; исправленный сценарий проверяет ответы. Для настоящего stress
также нужны ответы render-world: прежний sandbox их не давал и часто проверял лишь ожидание земли.
Дополнительно два свежих профиля игры на Metal/no-audio: smoke off не ready, HD smoke ready; при HORIZON_ACOUSTICS=1 + HORIZON_NO_AUDIO=1 акустика enabled/request=true, prepared=false, awaiting-backend. Выход через quit подтверждён. Это не прослушивание, не benchmark и не новый MP4. Правила API, пределы проверок и команды: ENGINE.md.
Двойные налёты и плотность следов
Перед продолжением читать RAID-STABILITY.md: два соответствующих класса
отключения теперь воспроизведены и исправлены — тик (1f28f83) и событие UI на втором
приказе (#122, order_raid/known_units); приёмка конкретного Windows-сеанса остаётся.
Текущий исправленный WASM stress проверяет две принятые волны, обслуживаемую плоскую геометрию
и 184 воздушные цели в конкретный момент; новый JSON
заменяет прежнее недостаточное доказательство покрытия. Лимиты fuel/memory не увеличивались.
86e1d272e в движке улучшает callback/fuel/error-chain diagnostics; ed7615d36 добавляет только
необязательную flat-world среду тестов и отказывает неожиданным ошибкам команд в Script.
Основная клиентская логика сцены этим test helper не заменяется.
692ae77: HD-треки ракет оптически плотнее на 12% через общий rocket() и cruise style.
SAM/RM48U, крылатые/сверхзвуковые/баллистические треки покрыты; сегментов, радиусов и времени не добавлено.
Пожары и дымка винтовых дронов не менялись. Обычные ванильные fallback-частицы этим множителем не меняются.
Пять существующих smoke-тестов прошли; прирост FPS и приёмка новой плотности на Windows не заявлены.
Последний общий прогон: core release190 + 6 сценариев, 7 ignored;
адаптер60 + 1 boundary + 4 сценария; лёгкий WASM26; тяжёлый WASM271; 5 тестов отчёта.
Нативные overlap-тесты — airdef-core/tests/scenarios.rs:
два стандартных массовых пресета и два налёта с разделением «Орешников», оба со встроенной записью.
Продолжение по пользовательскому HD-отключению: 1f28f83 заменяет полный known.retain при
каждом взрыве на удаление прежнего квадрата; семантическая эквивалентность проверена.
В диагностическом WASM max-menu ×2 раньше был trap tick1835 на 300M; после фикса полный
3602-tick прогон прошёл, max135273690. Гость отличается от baseline только временным trigger hook
состава меню и исправлением. Это измерение fuel, не прирост FPS. Артефакты/ограничения — в RAID-STABILITY.
cc48302 добавляет публичный native max-menu regression; 3b470dd закрепляет опциональный
engine test --fuel-profile (не новый SDK API и не увеличение лимитов).
cbd6067 исправляет сообщение о пустом соседнем складе, показывает типизированные запасы на M,
уточняет подсказку разведки и убирает устаревшую строку README о якобы отсутствующей доставке.
GROUND-STRIKES.md — инструкция с различием номеров своего С-400/склада и вражеской ПУ.
Default RM48U=0; ground-stock задаёт остаток, ground-load грузит одну; поздний трек не даёт fix.
ab737e7: actual WASM теперь 27 шагов с проверкой ошибки пустого соседнего склада.
После фикса полный core: 192 passed + 6 сценариев / 7 ignored; новый max-menu сценарий отдельно прошёл седьмым. Адаптер60 + 1 boundary + 4 сценария; light WASM27; engine /spin fuel-profile test прошёл.
Исходная контрольная точка этой серии
Исходное состояние этой серии: 2689d93; движок закреплён на
86b1014a9218e68041444cbf52db02a85107c815, уровень API 26. Рабочее дерево было чистым,
коммиты опубликованы. Текущий точный pin всегда смотреть в git ls-files --stage horizon,
а происхождение собранного пакета — в airdef-mod/target/horizon-mod/build-identity.json.
Четыре задачи были заказаны владельцем отдельно:
| Issue | Заказанный остаток |
|---|---|
| #120 | Конечная работа двигателя перехватчика, coast, энергия манёвра, согласованные эффекты |
| #118 | Типизированная доставка RM48U через ТЗМ; не создавать неподтверждённые носители |
| #121 | Переключение излучения, сохранение разрешённого внешнего целеуказания, разведка излучателей |
| #119 | Ослабление непрямого урона известным укрытием, ограниченная политика Unknown |
Исполнители работали в отдельных worktree; их проверенные коммиты перенесены в kimi-work.
В ветках codex/issue-* сохранены промежуточные версии; продолжать нужно от kimi-work,
где разрешены конфликты общего формата сохранений и сигнатур боевых функций.
Что уже сделано в предыдущей серии Codex
airdef-coreкомпилирует исходникиairdef-mod/src/sim/mod.rsчерез#[path]. Это единый источник симуляции, но разделение домена и представления ещё не завершено (#87).- Исправлены остаточные терминальные коллизии ответных ударов и скачок после world-query (#106/#117). Восстановление ответов мира не даёт права телепортироваться к плановой точке.
- Доработаны запись ответных ударов, launch one-shot и live-след (#109), lifecycle показа и часы отложенного звука (#88/#89). Полная унификация всех потребителей сцены ещё не закончена.
- Движок вынесен в отдельную репу; сборщик берётся из закреплённого форка, а не случайного PATH. Есть build identity, настоящий WASM contract и handshake доступности дыма API26 (#102/#104).
- Обновлён upstream Horizon 0.2.97, исправления очереди/инвалидации акустики сохранены. Измерительные fixtures, notices и лицензии добавлены; прослушивание и массовый CPU-тест ещё нужны (#96).
- Pipeline поддерживает authored overrides, manifest и экспорт итоговых габаритов в
sim/generated_bounds.rs. Навигация учитывает footprint. Generated output вручную не редактировать. - Улучшены модели наземной техники; степень сходства и материалы ещё промежуточные (#115).
Галерея:
MODEL-PILOTS.md,VEHICLE-MODELS.md; это не приёмка всех летательных аппаратов. 966b52f: раздельные ПУ С-400/С-500, роли Radar Search/Engagement/Height/Multi. Чужая подсказка разрешает огневому радару уточнение, но не подменяет собственное измерение.966b52f: резерв конкретной ячейки, отдельные крышки ТПК, выброс до включения двигателя. RM48U — условный игровой профиль С-400; AA и ground rounds делят четыре физические ячейки. Боеприпас списывается при выбросе, не при выдаче отложенного приказа; отмена не расходует его.- Конфигурация, маска использованных ячеек и положение крышек включены в save/replay. Smoke начинается в точке зажигания. С-500 ограничен двумя ракетами по числу ТПК; старое сохранение с четырьмя загруженными ракетами при загрузке ограничивается двумя.
2689d93: документированы источники архитектуры батареи, команды, скриншоты и границы визуальной проверки.
Проверки и воспроизведение
На 966b52f: 169 тестов ядра + 4 сценария прошли, 7 ignored; адаптер — 53 теста,
1 boundary и 4 сценария прошли; WASM contract — 21 шаг. Это исходная контрольная точка,
не результат новых четырёх задач. На 2689d93 был собран чистый пакет (airdef_dirty=false).
На итоговом объединённом коде 6e2d661 прошли:
airdef-core --release: 187 passed, 7 ignored, плюс 4 сценария.airdef-mod: 56 passed, 1 boundary, 4 сценария.- Настоящий WASM-пакет: 24 шага, включая команды излучения и typed ammunition.
gen_models.py --check: различий нет; в этой серии геометрия ассетов не менялась.
Полный core здесь запускался с --release; это не прогон всех семи ignored-тестов.
Отдельно выполнен сравнительный ignored-тест баланса (см. ниже).
cargo test --release --manifest-path airdef-core/Cargo.toml
cargo test --manifest-path airdef-mod/Cargo.toml
python3 tools/airdef.py test-wasm
python3 tools/gen_models.py --check
python3 tools/test_gen_assets.py
git diff --check
На этой macOS cargo находится в ~/.cargo/bin/cargo. GUI-проверки запускать с отдельным
HORIZON_HOME и рабочим каталогом тестового мира; не использовать реальные сохранения владельца.
Metal-проверки не подтверждают Windows/Linux, HDR всех платформ или прослушивание звука.
Скриншоты крышек подтверждают только их позу: полного видео выброса/зажигания пока нет.
Технические детали новой серии
#120: энергетика и единственный исход перехвата
MissileKind::burn_ticks задаёт конечное время горения: простой профиль равен 2/3 игрового
spec.life; для электрического P1-SUN используется весь срок жизни. Это не отдельная модель
топлива и не реальные кривые тяги. Missile::engine_lit — общий источник состояния мотора.
Три ветки скорости разделены явно: cold (age <= cold, множитель 0.97 с прежним минимумом 0.4),
powered (+accel, ограничение spec.speed), coast (множитель 0.995 без холодного минимума).
После выгорания доступный поворот масштабируется текущей скоростью; при падении ниже 25%
игрового максимума перехват заканчивается диагностикой MotorEnergyLost. Срок жизни остаётся
отдельным ограничением. Полноценной аэродинамики, гравитационной баллистики и расхода топлива по тяге нет.
Swept fuse учитывает движение обоих объектов за тик; Impact получает точку closest approach
и относительную скорость того же проверенного отрезка, включая удержание на Unknown.
Геометрию не заменяет отдельный бонус «встречная/догонная». Один остаточный pk зависит от
фактической скорости перехватчика и существующего состояния носителя/дальности.
Повторный штраф по stealth и старый штраф по абсолютной скорости цели удалены из этого броска;
обнаружение/сопровождение по-прежнему учитывают stealth в своих местах.
MotorBurnout и MotorEnergyLost записываются в scene journal; tally отличает потерю энергии.
Live и replay прекращают факел и новый дым по одному состоянию. Out::engine_stops немедленно
снимает голос мотора на burnout, не ждёт обычной трёхтиковой grace пропавшего источника;
при переходе на replay/export live-команды остановки отбрасываются вместе с live-выводом.
Перемотка восстанавливает моторное состояние из записи, включая возврат до выгорания.
При ревью был найден и исправлен недостижимый coast: ранний прототип проверял !lit,
что перехватывало и cold, и coast. Не переносить один 143d9b2 без последующих исправлений.
a7b8cb9 содержит отдельный тест точных правил скорости всех трёх фаз.
Сравнительная выборка только изменения #120, отдельно от #119/#118/#121:
baseline 2689d93, агентская итоговая ветка 508f9a0 (её исправление перенесено как a7b8cb9).
12 одинаковых seed, SAMS=20 SIZE=12: 204 цели в каждой версии;
baseline — 202 сбитых, 2 прорыва, 367 ракет; новое — 203, 1, 355.
Это небольшой регрессионный ориентир, не доказательство баланса всех типов/дальностей.
Данные каждого seed: CSV.
Расширенная сетка остатка #120 (ветка agent/issue-120-balance, коммит см. в git):
baseline 2689d93 против текущего e6a7000 (то есть с #118/#119/#121 и сменами движка, не только #120),
6 вариантов × 8 одинаковых seed, рейд SIZE=36 (46 целей: Gerbera 10, Shahed 13, Geran3 5, Х-101 5,
«Калибр» 4, Х-22 2, «Циркон» 1, «Искандер» 4, «Кинжаль» 2; «Орешник» harness исключает, как в one_raid):
batt10/20/50 (10/20/50 ЗРК, дистанция 500), near200 (20 ЗРК, пуск в 200), far1500 (20 ЗРК, пуск в 1500),
hills20 (20 ЗРК, холмы, 500). Кластер тот же MIX из 10 систем: SAMS=10 — по одному комплексу каждого типа.
Итого 2208 целей на сторону, 96 строк: CSV. Команда (игнорируемый тест
sim::balance::issue_120_grid, только harness-поля в Result, поведение симуляции не менялось):
SEEDS=1-8 cargo test --release --manifest-path airdef-core/Cargo.toml issue_120_grid -- --ignored --nocapture
Факты сетки (суммы по 8 seed каждого варианта, baseline → текущий): сбитые 297→301 (batt10), 331→334 (batt20),
357→363 (batt50), 313→318 (near200), 328→338 (far1500), 328→334 (hills20); прорывы 70→66, 37→34, 11→5,
53→48, 40→30, 39→33. Промахов по взрывателю заметно меньше (например far1500: 70→38). Расход ракет близок
(516→504 … 597→583). На холмах потери у рельефа: разбито 14 и 14, самоуничтожений у хребта 6→2.
Прорывы во всех вариантах — преимущественно баллистические и быстрые классы («Искандер», «Кинжаль», Х-22,
«Циркон»); у медленных Shahed/Gerbera прорывов почти нет (0–2 на вариант), у ложных Gerbera — ноль ни разу.
MotorEnergyLost не сработал ни в одном из 48 сценариев текущего кода, expired ≤ 1 на вариант: coast
существует, но бои на этих дистанциях заканчиваются раньше, чем сопротивление 0.995/тик опустит скорость
ниже порога 25% (и раньше выработки ресурса). Это измеренное свойство профиля, не подтверждённый дефект,
поэтому параметры не менялись. Путь потери энергии покрыт юнит-тестом регресса (regress.rs), а не этой сеткой.
SAMS=20 SIZE=12 cargo test --release --manifest-path airdef-core/Cargo.toml sim::balance::one_raid -- --ignored --nocapture
#118: один физический груз и восстанавливаемая заявка
ground_request у С-400 — заявка на одну RM48U. Рядом со складом остаётся прямая передача;
вне радиуса склада та же команда ground-load создаёт заявку, если есть запас и живая ТЗМ.
ТЗМ забирает одну ракету из конечного запаса, едет к исправному развёрнутому носителю и передаёт
её в общие ячейки. Если вытеснена ракета ПВО, ТЗМ везёт её обратно на склад. Generic reload
не создаёт RM48U. Ударная заявка имеет приоритет перед generic reload.
Поля ground_stock / air_48n6_stock на складе — остатки, на ТЗМ — физический груз двух типов.
Заявка, груз и марш сохраняются; оперативный job реконструируется после загрузки.
Передача запрещена во время движения, подготовки пуска, перезарядки, отказа ПУ и при отсутствии
вместимости. Разрушение очищает заявку/груз; repair не восстанавливает уничтоженный typed stock.
Проверены настоящее движение Sim.step по плоскому WorldSource, передача/возврат, истощение,
прерывание, несовместимость, заполненные ячейки, unload и полный state_text → apply_state
с продолжением маршрута. GUI-доставка на сложном рельефе ещё не проверена.
Новых носителей вроде Бука не добавляли: один публичный игровой профиль не даёт основания
приписывать его всем ЗРК. Обратный вывоз из удалённого носителя автоматизирован в продолжении выше.
Новая команда ground-cancel отменяет ожидающую заявку, но не возвращает уже забранный груз в ПУ.
ТЗМ вмещает два типизированных места RM48U/48Н6; прежние абстрактные generic reload-пакеты учитываются отдельно.
Вывезенная ракета возвращается в конечный запас; ПВО восстанавливается при заборе только из реально
имеющейся у ТЗМ ракеты 48Н6 и свободной ячейки. Резерв одной RM48U учитывается при выборе носителя удара.
#121: излучение отдельно от сети
radar_on — сохраняемая настройка, default=true для старых миров. Команда
/airdef radar <номер> on|off и кнопка карты меняют её; у launcher-only нет своего активного радара.
radar_up учитывает этот флаг. Off запрещает новые own measurements, но старые треки стареют
обычно и разрешённый сетевой приём сохраняется. Это не выключение корпуса/связи/ГСН ракеты.
Огневой радар, обязательный для отдельной ПУ, в Off не авторизует новые пуски.
Условный наблюдатель-противник раз в 20 тиков обнаруживает работающие радары в 1600 блоках. Это упрощённый игровой датчик по расстоянию, без LOS/радиопеленгации и ошибки оценки позиции. Контакт хранится как последняя наблюдённая точка и стареет за 6000 тиков. Off не стирает контакт, не телепортирует его вслед за носителем и не предотвращает раскрытие пуском/РЭБ.
В raid::radar_strikes удалён отдельный обход: раньше SEAD выбирал любую Radar и её настоящий
pos, даже если общий planner уже работал по наблюдениям. Теперь SEAD берёт те же last-known
контакты и их возраст. Явный sandbox-режим Aim::ObjectsAndSams по-прежнему выбирает расстановку
напрямую; не путать его с умным противником. Проверки покрывают необнаруженную молчащую РЛС,
смену позиции без наблюдения, повторное раскрытие, старение, relay freshness и seek.
Совместимость текстового состояния
Сохраняем строки sam расширением справа; индексы ниже включают слово sam как поле 0.
| Индекс | Значение |
|---|---|
| 22–24 | ground_rounds, ground_stock, air_48n6_stock |
| 25–27 | mode, radar_role, spent_cells |
| 28 | ground_request: 0/1, только С-400 |
| 29 | radar_on: true/false, отсутствие означает true |
| 30 | ground_return: true/false, резерв вывоза одной RM48U из С-400 |
| 31 | return_cargo: true/false, возвращаемая RM48U на ТЗМ |
Две агентские ветки сначала заняли поле 28 одновременно; при интеграции излучение перенесено
в 29, опубликованная совместимость заявки сохранена. Проверка полного roundtrip одновременно
содержит ожидающую доставку и выключенный радар. Новые значения входят в durable mark;
настройка излучения также в SamKey и ghost replay. Не переиспользовать эти позиции для нового поля.
#119: непрямой взрыв и укрытия
WorldCache::blast_cover читает текущую occupancy без запросов мира и без загрузки чанков.
Три луча к репрезентативным точкам корпуса дают общий множитель для корпуса и узлов;
для отмеченного объекта считается своя тройка. Известное укрытие оставляет 15% игрового
воздействия по перекрытому лучу, открытый — 100%; края/проёмы дают промежуточное значение.
Все лучи этого вызова читаются до отправки разрушения в движок. Исходный воксель пропускается,
чтобы контакт на поверхности стены не закрывал взрыв сразу во все стороны.
Unknown, stale и слишком старые секции считаются неразрешёнными. Ожидание равно нулю: для них сохраняется прежний неослабленный урон как консервативная игровая политика, а не утверждение, что там воздух. После ответа мира этот взрыв не пересчитывается. Нет очереди отложенного урона и повторного применения на world reply. Частичные блоки на этом этапе считаются занятым кубом; тонкая collision shape для blast отдельно не запрашивается. Прямой вооружённый контакт не ослабляется этим множителем.
Проверки sim::damage::cover_tests: стена/проём для корпуса, узлов и объекта;
Unknown и поздний ответ без повторного урона/запросов; прямое попадание; устаревшая геометрия;
пропуск исходного блока. Все четыре прошли в объединённом наборе.
#127: устойчивая очередь маршрутов после массового перенацеливания
Реализована только узкая часть issue: sim::ew::RouteJob — задание с ID дрона, новой целью,
ID цели-юнита, версией знаний и общим для тика снимком known_units (Arc). adapt по-прежнему
выбирает новые цели раз в 40 тиков, но маршруты теперь достраивает replan_retargets каждый
тик: одно задание или несколько, пока не исчерпан WORK_PER_TICK. Незавершённое задание
остаётся в Sim::retargets (FIFO) и не теряется; повторный выбор того же дрона заменяет задание
на месте, поэтому очередь не растёт.
Старый маршрут не очищается при перенацеливании: дрон следует готовому безопасному участку,
пока новое задание не получит маршрут. Если задание отменяется (дрон погиб, цель изменена снова,
юнит уничтожен), маршрут очищается как fallback — дрон летит прямо до следующего адаптивного тика.
Lancet, баллистика и decoy, как и раньше, летят прямо: для них задание не создаётся. В полёте
следующая известная цель берётся только если до неё есть разворот; цель сзади внутри радиуса
разворота пропускается, и дрон завершает текущий заход, а не получает невыполнимый маршрут.
Повторный план использует снимок разведки исходного выбора цели и известный рельеф той же версии
задания: KnownRelief (сетка и высоты) лежит в Arc внутри RouteJob, Sim::known_relief хранит
тот же Arc только для новых заданий. Завершение другой умной волны заменяет глобальный указатель,
но не снимок ожидающего job. Выборка рельефа, цены и кэш маршрутов в replan_retargets ключуются
версией знаний целиком — adapt tick плюс указатели на снимки known units и рельефа, — а не только
рельефом: два job с одинаковым рельефом, но разными known units, не делят price/route grid. За
пределами известной сетки — Unknown, новая карта не раскрывается.
Очередь — рантайм-состояние планировщика. Дроны и очередь запуска в текущий state_text не входят,
поэтому формат save не менялся и старые saves читаются как прежде; запись replay по-прежнему хранит
траектории, а не задания. Регрессии: mass_retarget_finishes_its_route_jobs_over_the_budget
(40+ ретаргетов, прогресс каждый бюджетный тик, старый маршрут до нового, нет пустых маршрутов)
и mass_retarget_is_deterministic_and_never_duplicates_a_drone (тот же seed — те же цели и
маршруты; повторный adapt не создаёт дублей).
#127, этап 1: честная EnemyPicture
Журнал intel.revealed: Vec<(id, место, тик)> заменён картой контактов: Intel.contacts: BTreeMap<u32, Contact>,
одна запись на юнита, последнее наблюдение побеждает. Контакт пишется только в момент раскрытия и
читает истину только тогда: пуск (mod.rs/strike.rs) → Seen::Launch; фиксация излучения
радаром рядом с чужим аппаратом (sight.rs, каждые 20 тиков, 1600 блоков) → Seen::Emission
(отдельно помечается emission — вера в собственный сенсор опирается только на неё); работающая
РЭБ (ew.rs) → Seen::Jamming; четвёртый канал — BSA: падение собственной боеголовки рядом с
известным юнитом (Sim::observe_outcomes, радиус 16) фиксирует destroyed либо обновляет «жив»
и место. Пока юнит незамечен, его перемещения, выключение радара, смена ролей/режима, расход БК,
урон и гибель картину не меняют — это метаморфные тесты sim::ew::picture_tests.
Из контактов решается всё: снимок picture_of_defences() на приказ и на адаптивный тик (newest first,
при равном тике — больший id, как у старого reversed-журнала), цели choose_target и SEAD
(radar_strikes — только Probable и свежее), ретаргет в полёте (цель «мертва» лишь по контакту с
destroyed; забытого контакта дрон не замечает), живость задания очереди (job_live — по картине,
не по истине). Уверенность — функция одного возраста: Confirmed ≤ 600, Probable ≤ 3000, Doubtful до
TTL 6000 тиков (Confidence, диагностика и SEAD её используют, цены — нет: консервативно считаются
все живые контакты).
Модель возможностей в price_grid_known читает только картину: роли — published kind.spec().roles
(смена /airdef roles невидима), дальности — ew::published_reach (без урона, фактора роли и
мачт истины), донор — любой живой контакт вида Radar в link range от наблюдённого места (роль
донора не наблюдаема: рабочий контакт РЛС предполагается кормящим сеть — это допущение, а не
знание; разрыв связи/гибель донора незамеченными картину не меняют). Собственный сенсор верится,
пока жива фиксация эмиссии; пуск при нуля эмиссионных фиксаций у С-400/С-500 читается как
launcher-only: без донорского контакта такая ПУ в ценовой сетке «слепа», с ним бьёт на дальность
донора. Горизонт РЛС игрока (self.horizons) из чужого планировщика убран: маскировка рельефа —
тайна обороны, цены по ней консервативнее и объяснимее.
Диагностика без GUI: Sim::enemy_picture() -> EnemyPictureReport (англ.): по каждому контакту —
наблюдение (канал, место, возраст), уверенность, живость, наблюдённое уничтожение, вера в сенсор,
наличие донора и текстовое «что планировщик из этого делает». Адаптация по-прежнему ограничена
статистикой секторов (best_bearing по kills/hits): новых всеведущих данных и усиления
характеристик нет.
Форматы не изменились: intel — рантайм (save хранит Layout, replay — траектории), durable-очередь
по-прежнему не делается. Измерения до/после на одних seed: docs/measurements/issue-127-enemy-picture-baseline.json
(0b645d77e) и issue-127-enemy-picture-result.json (эта ветка), bench — cargo run --release --example intel_bench.
big-smart и logistics побайтово совпали с базой; ew-adaptive изменился (kills 62→59/61→59/61→56,
hits 3→7/5→7/7→8, потери ЗРК 1→3/4→3/4→4) — фиксируется как факт перехода на честную картину,
балансные параметры не трогали. Регрессии: метаморфные hidden_changes_do_not_move_the_picture_or_its_decisions,
a_new_observation_is_what_updates_the_picture, launcher_only_is_blind_in_the_picture_without_a_donor
(донор любой роли; разрыв связи наблюдённый/нет), destruction_moves_the_picture_only_when_watched,
confidence_is_a_function_of_age_alone, enemy_picture_report_describes_observation_and_belief,
an_impact_watches_the_kill_and_updates_only_the_picture (BSA end-to-end), обновлённые SEAD/#4/#21/#122
и обе регрессии очереди. Остаток #127: durable save queue (новый формат save), полный structured intent,
GUI поверх enemy_picture(), оценка баланса ew-adaptive.
Что следующему агенту проверить в первую очередь
- На Windows: обновление сабмодуля через
all, сохранение/перезаход, рабочий цикл игры. - В GUI: полный холодный пуск крупным планом; карточка On/Off; маршрут ТЗМ по сложной местности; разрушение стены одним взрывом и защита от того же взрыва по исходной геометрии.
- Прослушать прекращение голоса двигателя при burnout и seek/экспорт вокруг этой границы.
- Расширенная балансировка #120 (батареи/дальности/классы) записана в
measurements/issue-120-grid.csv; остаются прослушивание голоса мотора и платформенная приёмка. - Продолжить оставшиеся архитектурные #87/#88/#89/#91/#102 и художественный #115 по статусной таблице.
Это незавершённая платформенная/визуальная приёмка, а не отсутствие перечисленного выше кода. Прогон четырёх задач не запускал GUI/MP4. В продолжении API27 есть два коротких игровых smoke, но новый MP4, прослушивание и платформенная приёмка Windows/Linux по-прежнему не выполнены.
Правила продолжения
- Не закрывать issue даже после исправления; сообщать проверенный объём и остаток в комментарии.
- Не переписывать gitlink на плавающую ветку. Изменения движка сначала публикуются отдельно.
- Не путать наблюдённый трек с настоящим положением цели и
own_tickс relay. - Не менять live-мир при seek; replay воспроизводит последствия, не применяет урон повторно.
- Не считать Unknown свободным пространством или подтверждённой стеной без явной игровой политики.
- Новые числа — игровой масштаб, не реальные характеристики вооружения.
- Токен Gitea читать из локального файла во время запроса; не коммитить и не печатать.