Завершить контентную архитектуру этапа 4
This commit is contained in:
+16
-4
@@ -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 нельзя называть принятым внешними тестировщиками.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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` остаётся
|
||||
открытым: требуются две независимые ручные сессии на каждый уровень.
|
||||
Reference in New Issue
Block a user