Завершить контентную архитектуру этапа 4

This commit is contained in:
2026-08-18 22:03:38 +03:00
parent 2ddac2775f
commit 5051f316fe
39 changed files with 1954 additions and 259 deletions
+16 -4
View File
@@ -6,7 +6,7 @@
## 1. Текущее состояние
Инженерная часть этапов 2 и 3 завершена:
Инженерная часть этапов 2–4 завершена:
- реализованы 10 последовательных уровней;
- работают движение, прыжок и мобильное multitouch-управление;
@@ -25,6 +25,11 @@
- весь Canvas UI, десять названий и подсказок доступны на русском и английском; язык хранится отдельно и не меняет progress/control settings;
- Stage 3 focused matrix: 14/14 методов без retries; UI contracts GREEN на 20:9, 16:9 и логическом 16:10 tablet viewport;
- final Stage 3 Android 16 matrix: `OK (30 tests)` без filters/retries за `1,409.366 s`; все intended routes 1–10, recovery/stress, lifecycle, restart и UI/persistence contracts GREEN;
- все десять уровней и RU/EN copy перенесены из Java-констант в versioned JSON с `formatVersion=1` и `physicsVersion=1`;
- общий pure-Java loader/validator используется Android runtime и desktop tooling; повреждённая геометрия, версия или ссылка на signal останавливает build до APK packaging;
- deterministic desktop exporter создаёт десять SVG и единый coordinate TSV с byte-identical результатом для неизменного каталога;
- acceptance bots сохраняют versioned command/checkpoint trace и SHA-256 trace ID без wall-clock или прямых mutation-команд;
- final Stage 4 Android 16 matrix: `OK (30 tests)` без filters/retries за `1,096.448 s`; вся Stage 3 gameplay/UI матрица GREEN после JSON-миграции;
- final integrated Android 16 matrix: 16/16 методов, 36 игровых выполнений, lifecycle и restart; wall time `9:32.496`;
- финальный debug APK проверен на Android 16: установка, холодный запуск, ZIP-структура и v1/v2-подписи корректны.
@@ -182,7 +187,7 @@ Performance-оговорка: исторический критерий этап
8. холодный запуск и проверка crash-буфера;
9. ручной smoke-тест уровня 1 и одного сложного комбинированного уровня.
## 5. Завершение этапов 2–3 и следующая итерация
## 5. Завершение этапов 2–4 и следующая итерация
Инженерный scope этапа 2 закрыт: уровни 1–10 проходят intended route по три раза, уровень 8 имеет restart-free recovery, уровень 10 использует все три тела и сохраняет тело на lift-плите, а restart возвращает чистое состояние. Передаваемые результаты — обновлённый проект, проверенный APK и [`STAGE2_QA_REPORT.md`](STAGE2_QA_REPORT.md).
@@ -190,7 +195,14 @@ Performance-оговорка: исторический критерий этап
неблокирующее обучение, полный result flow и RU/EN локализация. Совместный
device-gate проверяет сохранность progress/settings, а UI contracts выполнены на
20:9, 16:9 и tablet-sized экране. Подробный итог находится в
[`STAGE3_QA_REPORT.md`](STAGE3_QA_REPORT.md). Следующая инженерная итерация — этап
4, перенос описаний уровней в валидируемый формат и инструменты контента.
[`STAGE3_QA_REPORT.md`](STAGE3_QA_REPORT.md).
Этап 4 завершён: каталог загружается из десяти JSON-ресурсов, общий validator
запускается до Android packaging, а desktop-команда создаёт deterministic SVG/TSV
preview. Боты публикуют versioned trace с идентификатором для диагностики
регрессий; неизменность gameplay подтверждена полным Android regression.
Контракт и evidence находятся в [`STAGE4_CONTENT_FORMAT.md`](STAGE4_CONTENT_FORMAT.md)
и [`STAGE4_QA_REPORT.md`](STAGE4_QA_REPORT.md). Следующая инженерная итерация —
этап 5, самостоятельный художественный стиль, анимации и звук.
Параллельно внешний QA должен выполнить `goon-game-human-playtest`: по две ручные сессии каждого уровня без подсказок разработчика. До получения этого протокола milestone Vertical Slice нельзя называть принятым внешними тестировщиками.
+139
View File
@@ -0,0 +1,139 @@
# Stage 4: формат уровней и контентные инструменты
## Назначение
Все десять уровней находятся в Android-independent модуле `level-format` и
загружаются из `level-format/src/main/resources/levels`. Runtime, unit-тесты и
desktop-команды используют один и тот же JSON parser, catalog loader и validator.
Изменение геометрии или текста не требует изменения игрового Java-кода.
Текущие версии контракта:
- `formatVersion: 1` — схема JSON;
- `physicsVersion: 1` — набор физических допущений, для которого создан уровень;
- bot `traceVersion: 1` — формат журнала acceptance-команд.
Неизвестная версия отклоняется. Поддержка новой версии должна добавляться явно,
вместе с migration/tests, без молчаливого fallback.
## Каталог
`levels/index.json` задаёт точный порядок уровней и содержит те же версии:
```json
{
"formatVersion": 1,
"physicsVersion": 1,
"levels": ["level-01.json", "level-02.json"]
}
```
Имена в каталоге должны быть уникальны, ресурсы должны существовать, а поле
`number` каждого загруженного уровня должно соответствовать позиции в списке.
## Схема уровня
Минимальный характерный пример:
```json
{
"formatVersion": 1,
"physicsVersion": 1,
"number": 3,
"copy": {
"en": {"title": "Dead Weight", "hint": "Leave a body on the plate."},
"ru": {"title": "Мёртвый груз", "hint": "Оставьте тело на плите."}
},
"world": {"width": 2050, "height": 1080},
"spawn": {"x": 130, "y": 760},
"goal": {"x": 1810, "y": 770, "width": 86, "height": 110},
"targets": {"deaths": 1, "seconds": 50},
"platforms": [
{"box": {"x": 0, "y": 880, "width": 2050, "height": 200}}
],
"hazards": [
{"type": "SAW", "box": {"x": 565, "y": 760, "width": 92, "height": 92},
"impulseX": 0, "impulseY": -150}
],
"switches": [
{"type": "PRESSURE", "box": {"x": 540, "y": 860, "width": 150, "height": 20},
"signalId": "gate", "durationSeconds": 0, "requiredMass": 1}
],
"doors": [
{"box": {"x": 1110, "y": 570, "width": 78, "height": 310},
"requiredSignals": ["gate"]}
]
}
```
Поддерживаемые значения:
- hazards: `SPIKES`, `SAW`, `FREEZER`, `ELECTRIC`, `PIT`;
- switches: `PRESSURE`, `TOGGLE`, `TIMED`;
- static platform: только `box`;
- moving platform: `box`, ненулевой `travelX` или `travelY`, положительный
`periodSeconds`, опциональный `controlId`;
- electrical hazard может публиковать `relayId`;
- door перечисляет все `requiredSignals` (AND-семантика).
## Валидация
Валидатор проверяет до упаковки APK:
- известные format/physics versions;
- конечные положительные размеры мира и объектов;
- spawn, goal, платформы, hazards, switches, doors и конечную позицию moving
platform внутри мира;
- наличие хотя бы одной платформы;
- неотрицательный target deaths и положительный target seconds;
- непустой signal ID, положительный required mass и положительную длительность
`TIMED`;
- существование каждого `door.requiredSignals` и `platform.controlId` среди
сигналов switches/relays;
- целостность index, уникальность ресурсов и последовательную нумерацию.
Ошибка содержит путь, например:
```text
level-03.json.doors[0].requiredSignals[0] references missing signal 'missing'
```
Команда:
```sh
./gradlew :level-format:validateLevels
```
`app:preBuild` зависит от неё. Для проверки временной копии каталога можно
передать `-PlevelDirectory=/absolute/path/to/levels`.
## Детерминированный preview
```sh
./gradlew :level-format:previewLevels
```
Результат находится в `level-format/build/level-previews`:
- `level-01.svg` … `level-10.svg` — world-space схема spawn/goal, платформ,
hazards, switches и doors;
- `catalog.tsv` — координаты, размеры, travel и signal/control references.
Вывод использует стабильный порядок и locale-independent числа. Два запуска на
неизменном каталоге должны давать byte-identical файлы.
## Bot trace
Каждый generic и level-8 acceptance-run возвращает `traceJson()` и `traceId()`.
Trace содержит:
- `traceVersion`, level, `levelFormatVersion`, `physicsVersion` и scenario;
- outcome;
- события `START`, `INPUT`, `CHECKPOINT`, `FINISH`;
- только simulation time, публичный input и наблюдаемое состояние игрока/дверей.
Wall-clock, device identifiers и прямые mutation-команды не записываются.
`traceId` — SHA-256 точного UTF-8 JSON и всегда включён в failure diagnostic.
Stage 4 сохраняет воспроизводимый артефакт расследования, но намеренно не добавляет
runtime replay executor.
+90
View File
@@ -0,0 +1,90 @@
# Stage 4 QA report
Дата: 18 августа 2026 года
Версия: `0.7.0-stage4-content` (`versionCode 8`)
## Результат
Этап 4 «Контентная архитектура и инструменты» завершён инженерно:
- уровни 1–10 перенесены из Java-констант в versioned JSON;
- Android runtime и desktop tools используют общий pure-Java loader/validator;
- build отклоняет повреждённый контент до APK packaging;
- deterministic SVG/TSV preview экспортирует все десять уровней;
- acceptance bots формируют versioned JSON trace и SHA-256 trace ID;
- исходная геометрия, физика, тексты и gameplay progression сохранены;
- полный накопительный Android regression прошёл без filters/retries.
## TDD evidence
Последовательные RED→GREEN slices:
1. отсутствовали loader и versioned fields → добавлен JSON loader и immutable model;
2. неизвестный format принимался → fail-closed version contract;
3. ссылка двери на отсутствующий signal не диагностировалась → path-specific reference check;
4. platform с нулевой шириной принималась → geometry/world-bounds validation;
5. parser не поддерживал полный набор механик → vocabulary всех текущих уровней;
6. отсутствовал index/catalog loader → ordered resource catalog;
7. RU copy возвращала EN fallback → localized copy перенесена в JSON;
8. отсутствовала desktop-команда → validate/preview tool;
9. preview command возвращала usage → byte-identical SVG/TSV output;
10. отсутствовала Gradle task → validation wired before `app:preBuild`;
11. отсутствовал versioned bot trace → deterministic Result trace + SHA-256 ID;
12. bespoke level-8 runner не трассировался → reusable tracing driver;
13. `TIMED` с нулевой длительностью принимался → positive duration validation.
Отдельно доказан build-time отказ на временной копии каталога с ошибкой:
```text
level-03.json.doors[0].requiredSignals[0] references missing signal 'missing'
```
Канонический каталог при этом не изменялся.
## Автоматические проверки
Integrated JVM/build gate:
```sh
./gradlew :level-format:test testDebugUnitTest lintDebug \
assembleDebug assembleDebugAndroidTest :level-format:previewLevels
```
Результат: `BUILD SUCCESSFUL`; parser, catalog, validator, tooling, localization,
bot trace и прежние unit contracts GREEN. Выполнено 52 app JVM-теста и 2
`level-format` теста, failures/errors = 0. APK содержит `levels/index.json` и
десять JSON-файлов уровней.
Device acceptance:
- AVD: `medium_phone`, Android 16 / API 36, 1080×2400;
- команда: unfiltered `am instrument` всего debug test APK;
- результат: `OK (30 tests)`;
- wall time: `1,096.448 s`;
- один процесс, без class/method filters и retries;
- GREEN: intended routes 1–10, level-8 stress/recovery, level-10 delayed
traversal, level-5, lifecycle, restart, controls, accessibility, RU/EN,
onboarding/result и Stage 3 acceptance contracts.
Первая попытка запуска на принудительном CPU SwiftShader была остановлена как
неограниченная ошибка среды: эмулятор расходовал около 678% host CPU и дошёл
только до второго теста за восемь минут. Она не содержала product failure и не
считается RED/GREEN или retry. Финальный результат получен на стандартном
host-rendered профиле AVD.
Final APK inspection:
- `unzip -t`: ошибок нет;
- package: `com.projectsacrifice.game.debug`;
- `versionCode=8`, `versionName=0.7.0-stage4-content-debug`;
- `apksigner`: v1=true, v2=true, один стандартный Android Debug signer;
- clean install: `Success`;
- cold start: `Status: ok`, `LaunchState: COLD`, 401 ms;
- `MainActivity`: `RESUMED`, visible, reportedDrawn;
- Android crash buffer после запуска пуст.
## Внешние gates
Как и в предыдущих этапах, automation доказывает техническую проходимость, но не
понятность головоломок новому игроку. Bead `goon-game-human-playtest` остаётся
открытым: требуются две независимые ручные сессии на каждый уровень.