Validate the actual hmod in the pinned host sandbox (#102)
build / mod (push) Failing after 19s

Keep runtime compilation in an opt-in CI tier; run asset consistency on regular builds. Sandbox checks do not substitute for GPU, capture or maximum-scenario budgets.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
2026-11-02 21:01:07 +03:00
parent ad96018c0a
commit dedb0213c7
5 changed files with 94 additions and 1 deletions
+2
View File
@@ -32,6 +32,8 @@ jobs:
run: nice -n 19 python3 tools/airdef.py fork
- name: Проверка закреплённого checkout
run: python3 -m unittest discover -s tools -p test_engine_checkout.py
- name: Проверка generated assets
run: python3 tools/gen_models.py --check
- name: Тесты ядра без SDK
working-directory: airdef-core
run: |
+20
View File
@@ -0,0 +1,20 @@
# Heavy sandbox runtime is opt-in on the shared 2-core runner.
name: wasm-contract
on:
workflow_dispatch:
env:
CARGO_BUILD_JOBS: "1"
CARGO_INCREMENTAL: "0"
jobs:
contract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Rust
run: |
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Actual WASM contract
run: nice -n 19 python3 tools/airdef.py test-wasm
+46
View File
@@ -0,0 +1,46 @@
# Actual .hmod in the engine sandbox, with default fuel/memory limits.
# Missing render replies exercise fallback paths, not GPU correctness.
[[step]]
ticks = 2
frames = 2
expect.commands = ["airdef"]
[[step]]
command = "airdef deploy patriot"
[[step]]
command = "airdef deploy s400"
[[step]]
command = "airdef wave mix 40 1500"
[[step]]
ticks = 240
frames = 120
[[step]]
command = "airdef clear"
[[step]]
ticks = 140
frames = 10
[[step]]
command = "airdef replay stop"
[[step]]
title-screen = true
ticks = 2
frames = 2
[[step]]
player = { x = 100.5, y = 64.0, z = 100.5 }
clock = { game-time = 500, day-time = 6000 }
ticks = 3
frames = 3
[[step]]
reload = true
ticks = 2
frames = 2
expect.commands = ["airdef"]
+13
View File
@@ -38,3 +38,16 @@ python tools/airdef.py play
Рядом с airdef.hmod записывается build-identity.json: SHA AirDef, pin/identity движка,
SHA256 WIT и артефакта. API-диагностика остаётся в `python tools/airdef.py check`.
Нативные тесты не заменяют отдельную игровую проверку графики, звука и экспорта.
## Проверка настоящего WASM
`python tools/airdef.py test-wasm` собирает `.hmod` и запускает `tests/*.toml` в настоящем
runtime закреплённого движка. Используется временное storage; игровые миры не нужны.
Сценарий проверяет загрузку/регистрацию команды, два комплекса, налёт, tick/frame,
выход из мира и перезагрузку мода. Действуют лимиты runtime: 64 MiB linear memory,
300 млн fuel на tick, 10 млн на frame, 1 млрд на event, 2 млрд на init.
Это проверка ABI и fallback при отсутствии render replies; она не измеряет GPU,
не подтверждает акустику/MP4 и не заменяет максимальный боевой полигон.
В CI проверка assets входит в лёгкий build. Wasmtime tier вынесен в ручной workflow
`wasm-contract`, чтобы не компилировать тяжёлый runtime на каждом push слабого общего runner.
+13 -1
View File
@@ -8,6 +8,7 @@
python3 tools/airdef.py play [аргументы игры] # запустить форк на папке игры
python3 tools/airdef.py all # всё по порядку: fork, game, mod, install
python3 tools/airdef.py check # совпадают ли форк, упаковщик, уровень API мода и ресурсы
python3 tools/airdef.py test-wasm # собрать и прогнать настоящий .hmod в песочнице без GPU
Переменные окружения: HORIZON_HOME (папка игры), HORIZON_RESOURCES (подготовленные ресурсы
Minecraft 26.3), AIRDEF_GAME (готовый horizon-client вместо собранного).
@@ -138,6 +139,17 @@ def hmod() -> Path:
sys.exit("Нет airdef.hmod: python3 tools/airdef.py mod")
def test_wasm():
"""The real component and host ABI, with the engine's normal fuel/memory limits."""
mod()
build_env = dict(os.environ)
build_env["PATH"] = str(Path(cargo()).parent) + os.pathsep + build_env.get("PATH", "")
# `full` is the packer's default feature; the lightweight release packer has no runtime.
run([cargo(), "run", "--locked", "--manifest-path", FORK / "Cargo.toml",
"-p", "cargo-horizon-mod", "--", "horizon-mod", "test", "--hmod", hmod()],
cwd=ROOT / "airdef-mod", env=build_env)
def install():
home = game_home()
mods = home / "horizon-mods"
@@ -281,7 +293,7 @@ def main():
if len(sys.argv) < 2:
sys.exit(__doc__)
cmd, rest = sys.argv[1], sys.argv[2:]
steps = {"fork": fork, "game": game, "mod": mod, "install": install, "check": check}
steps = {"fork": fork, "game": game, "mod": mod, "install": install, "check": check, "test-wasm": test_wasm}
if cmd == "play":
play(rest)
elif cmd == "all":