From 4cb82556de31268d2bde73948dd1ff1b6c02f162 Mon Sep 17 00:00:00 2001 From: Emil <65846814+emil28092005@users.noreply.github.com> Date: Fri, 18 Sep 2026 05:55:29 +0300 Subject: [PATCH] Preserve source locations across cached imports and record MVP acceptance --- docs/ARCHITECTURE.md | 2 + docs/IMPLEMENTATION.md | 26 + docs/validation/README.md | 2 +- .../checkpoint5-linux-2026-09-18/README.md | 38 ++ .../checkpoint5-linux-2026-09-18/blender.json | 70 +++ .../editor-blender.png | Bin 0 -> 93699 bytes .../first-run.json | 20 + .../checkpoint5-linux-2026-09-18/offline.json | 12 + .../playable-exports.json | 452 ++++++++++++++++++ .../recovery.json | 36 ++ src/assets/asset_data.cpp | 22 +- src/assets/asset_pipeline.cpp | 3 +- tests/assets_pipeline.cpp | 99 ++++ tools/verify_blender_roundtrip.py | 21 +- 14 files changed, 797 insertions(+), 6 deletions(-) create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/README.md create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/blender.json create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/editor-blender.png create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/first-run.json create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/offline.json create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/playable-exports.json create mode 100644 docs/validation/checkpoint5-linux-2026-09-18/recovery.json diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 7709bb4..5d4145a 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -47,6 +47,8 @@ Entry function плагина согласует версию интерфейс `TypeId/FieldId` описывают смысл сохраняемых данных и не зависят от имени C++, offset, порядка членов или `typeid`. Layout fingerprint нужен для совместимости конкретной сборки. Переименование сохраняет ID; изменение смысла/единиц требует миграции. Отсутствующий плагин не уничтожает неизвестные записи. Lua позже использует явно экспортированный runtime API и проверяемые handles; наличие metadata не делает каждый native-метод доступным MCP или Lua. +MVP доставляет декларативные шаги миграции через тот же schema manifest: `default`, `scale` и `require_manual`. Вся схема и правила проверяются до публикации Player/schema generation. Старые версии открываются как opaque data; миграция выполняется явно командой `component.migrate` с revision, одним Undo и записью recovery. Inherited component изменяется в исходной сцене; sparse overrides других экземпляров требуют явного обзора при изменении смысла или единиц. Произвольный C++ migration callback в Editor не загружается. Контракт и проверяемый пример — в [Manual](manual/scripting/api.md#editor-data-migrations). + ## 5. Авторские данные, сцены и редактор — принято Пользователь работает со сценами, объектами, компонентами и ресурсами. Сцены допускают **вложенные экземпляры шаблонов**. JSON хранит ссылку на исходный шаблон и **sparse overrides** — только локальные отличия, с устойчивыми адресами полей/объектов. Подписи и пути не являются идентичностью. Для добавления, удаления и изменения вложенного содержимого нужны явные операции; потерянная цель override становится диагностируемым конфликтом, а не молчаливым удалением данных. diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index b703138..fd4b4c2 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -261,3 +261,29 @@ including the launcher fix, and proceeded to real native Release exports. That r predates checkpoint 5's new authoring/diagnostic changes, whose Windows checks remain separate. The next full Windows build enables the optional diagnostic module too. No MVP tag is claimed at this checkpoint. + +## Checkpoint 5 acceptance and source relocation correction + +The clean `0f34b03` Linux checkout built offline with 20/20 CPU tests, then created +and rendered a fresh project using the Manual's command. Both checked-in games were +exported in Release, moved outside the SDK into Unicode paths and run for 120 frames +with source projects hidden; Khronos validation was active with no errors. All seven +recovery scenarios and the real Blender/live-Editor round-trip passed. Exact inputs, +hashes and limits are in [the Linux acceptance record](validation/checkpoint5-linux-2026-09-18/README.md). + +A subsequent source-relocation regression was reproduced and corrected: a cache-hit +rename/move updated the logical source pointer while retaining the historical payload +path, so the new freshness check incorrectly kept the asset stale. Publication now +stores both paths atomically. Earlier pointers remain readable by resolving the +payload relative to the moved logical source; immutable content generations and cache +keys are unchanged. Regression cases cover PNG, Blender bundles and external glTF +buffers/images, with both new and legacy pointer records. Full Linux integration +remains 34 passed / 1 explicit Wayland skip / 0 failed; targeted ASan/UBSan asset/cook +checks passed 2/2 after this correction. + +Windows run `35299805623` at `e0b9651` completed successfully, including both Release +integration exports, incremental C++ rebuilding, and the two checked-in games with +Unicode relocation. It used SwiftShader without the Khronos validation layer and is +functional software-Vulkan evidence, not a physical-GPU benchmark. Checkpoint 5's +newer Windows graphics/export run and the relocation correction have their own +revision-specific gates. diff --git a/docs/validation/README.md b/docs/validation/README.md index 32d5053..cf90db2 100644 --- a/docs/validation/README.md +++ b/docs/validation/README.md @@ -2,11 +2,11 @@ These files preserve bounded checks and their inputs. Each record states its source revision or working-tree limitation; a passing record does not certify later commits or every supported platform. +- [Checkpoint 5 Linux acceptance](checkpoint5-linux-2026-09-18/README.md): clean offline source build, first Editor launch, exact-candidate standalone games and live Blender checks. - [Linux offline build](offline-linux-2026-09-18.json): a fresh build from committed `d834cfad67cd81d8c4998b90c16791361ca8c0f8`, prefetched dependency inputs, and a network namespace without external connectivity. CPU tests ran; graphics execution is separate. - [Linux Release export and rendering](linux-release-2026-09-18/README.md): two relocated playable games, Unicode paths, imported Blender geometry, raw frame profiles and exact package hashes. This measurement used an evolving working tree around `5ac4db4`, not a clean checkout of the current commit. - [Live Blender reimport](blender-live-linux-2026-09-18.json): actual Blender 4.5.3 exports and two live Editor instances, preserving authoring/gameplay/physics while refreshing geometry. - [Editor recovery](editor-recovery-linux-2026-09-18.json): killed Editor, explicit journal recovery, failed save/import/build and failed Player startup with last-good preservation. -- [Native window boundaries](native-window-linux-2026-09-18.json): XWayland lifecycle, isolated Unicode clipboard, SDL text-input rectangles and the explicit native Wayland restore limitation. - [Linux native window and input boundaries](native-window-linux-2026-09-18.json): XWayland resize/minimize/restore and SDL text-input-area checks; isolated Xvfb Unicode clipboard roundtrip with restoration. Native Wayland restore remains unconfirmed; real OS IME composition was not tested. The Linux records do not establish Windows correctness, physical Windows GPU performance, native Wayland restore support, or editor responsiveness under large workloads. Windows CI artifacts record their own commit and software Vulkan results. A skipped compositor operation is not a completed acceptance check. diff --git a/docs/validation/checkpoint5-linux-2026-09-18/README.md b/docs/validation/checkpoint5-linux-2026-09-18/README.md new file mode 100644 index 0000000..d821614 --- /dev/null +++ b/docs/validation/checkpoint5-linux-2026-09-18/README.md @@ -0,0 +1,38 @@ +# Checkpoint 5 Linux acceptance + +These functional checks used the engine source at +`0f34b036313c011861dbfd5828ed45c4f7940b05`. They supplement the earlier Release +performance baseline; export runs overlapped compilation and are not benchmarks. + +- [Clean offline build](offline.json): committed source extracted with `git archive`, + empty build directory, external networking disabled, prefetched dependencies and + toolchain. Full default native build and 20 CPU tests passed in 205.77 seconds. +- [First Editor launch](first-run.json): that freshly built default Editor created + a new 3D project following the Manual's command, copied the correct gameplay + templates and rendered three native X11/Xvfb frames before closing successfully. +- [Both standalone games](playable-exports.json): the real Editor produced Release + packages, verified package hashes, moved them outside the SDK into Unicode paths, + hid source projects, then validated and rendered 120 frames per game without + scene/asset overrides. RTX 2080 Ti, Khronos validation active, zero validation + errors. The packages contained 17 files for 2D and 22 for 3D. +- [Blender round-trip](blender.json): unmodified Blender 4.5.3 and the actual live + Editor probe updated two instances while preserving IDs, transforms, tint, physics, + opaque gameplay and document revision. Removed outputs kept the last good visible + generation. The verification harness was updated to distinguish source freshness + from active-generation preservation; its separate hash is included in the report. + +- [Editor recovery](recovery.json): seven failure/recovery scenarios passed against + the same source revision and recorded Editor hash: interrupted process, explicit + journal recovery, failed save/import/build/Player startup, and subsequent recovery. + +The integrated build with optional diagnostics also passed 34 tests with one +explicit native Wayland restore skip; the sanitizer configuration passed 18/18. +These results describe Linux. Windows has separate CI and export evidence. +Native OS IME composition and moving between physical monitors were not exercised; +widget composition, DPI and native SDL input-boundary tests have narrower scope. + +The following image is an actual Editor capture from the Blender probe, after +rename/geometry reimport. It is not the generated UI reference. The test scene has +two placed instances and no selected object. + +![Native Faset Editor after Blender reimport](editor-blender.png) diff --git a/docs/validation/checkpoint5-linux-2026-09-18/blender.json b/docs/validation/checkpoint5-linux-2026-09-18/blender.json new file mode 100644 index 0000000..08212af --- /dev/null +++ b/docs/validation/checkpoint5-linux-2026-09-18/blender.json @@ -0,0 +1,70 @@ +{ + "source_commit": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "recorded_at_utc": "2026-09-18T02:45:37.886125+00:00", + "platform": "Linux-7.0.0-31-generic-x86_64-with-glibc2.43", + "working_tree_diff": [ + " M tools/verify_blender_roundtrip.py" + ], + "candidate_binaries_note": "Existing integrated Debug binaries from checkpoint5 build; SHA256 captured after this run. Engine source unchanged; verification harness has explicit freshness assertions patched after candidate commit.", + "command": "python3 tools/verify_blender_roundtrip.py --blender .cache/blender/blender --editor build/linux-debug/faset_editor --ui-probe build/linux-debug/faset_blender_editor_probe --output .cache/blender-final-candidate/verified", + "hashes": { + "build/linux-debug/faset_editor": "04baf2ec09f3f097657553d5b862ba5c90749d2cdbf4a239c6b92a45dee56ee9", + "build/linux-debug/faset_blender_editor_probe": "141433f89ae20202a16597525bfdcd59586bf17ab72ada29104ca324857b5842", + ".cache/blender/blender": "5e15447670804c855fef6033ac9b8fd82696b762095becf0df79b1c2f8034397", + "tools/verify_blender_roundtrip.py": "ce3bb9d02500a6fe8becef23a7c966449a13d7d9d00d92f2177c0c431ac7f930", + "tests/blender/generate_fixture.py": "6d7951c0d35b8e4e71b81644af31d66af4e395511d929452a178f81ba30e487b", + "tests/editor_blender_reimport_probe.cpp": "0474276d04e8c18c1d2d8d55ff2cff8cfabba815bb3428af51394e22649e3923", + "tools/blender_addon/__init__.py": "e1a44aed99e09a6ad208966eb2e3808110d87311f5721774575a14471ed852e2", + "tools/blender_addon/bundle.py": "f0fd9707b853662be00d08440ab9cc65928cec5a8e053561834bd64fd43d1488", + "dependencies.lock.json": "0583b24061dfc6ba3db76be89ceeaf408cac18d237773833204074af5d8a81d5", + ".cache/blender-final-candidate/verified/fixture/initial/manifest.json": "830818ee07a003aad63ed1345b0179852b9de1ab9a322142bd23e50fb501ba7d", + ".cache/blender-final-candidate/verified/fixture/removed/manifest.json": "45e07ec2c4ae98c6a9b74a1b60089a2da1ff2ade1b9822d048b33de640d80665", + ".cache/blender-final-candidate/verified/fixture/renamed/manifest.json": "dfbe56099ec026eaedd4a25218f3e3270ac075a933d77d37f8b784963792250c", + ".cache/blender-final-candidate/verified/fixture/initial/payload/81fdfbd230989c9f4985963cddf63edcfb89c13a0ef4a9265c853006830be488.glb": "81fdfbd230989c9f4985963cddf63edcfb89c13a0ef4a9265c853006830be488", + ".cache/blender-final-candidate/verified/fixture/removed/payload/3a9c9671bc1804454227d14f38705b9deacfd95492bf0aea1d2754ba198f128b.glb": "3a9c9671bc1804454227d14f38705b9deacfd95492bf0aea1d2754ba198f128b", + ".cache/blender-final-candidate/verified/fixture/renamed/payload/568624163d90cf20f71629ca1cb11f128898260bc521ae66bfc1a3ae6bf4b3e6.glb": "568624163d90cf20f71629ca1cb11f128898260bc521ae66bfc1a3ae6bf4b3e6", + ".cache/blender-final-candidate/verified/live-editor/initial.ppm": "363273f070f212580088d52b273baf94cebd62410bb9cf775dee14792f18bd99", + ".cache/blender-final-candidate/verified/live-editor/renamed.ppm": "973a99aa43d4e32345bba023ec34dd443db9dc747590c128f996f5f258fd9038", + ".cache/blender-final-candidate/verified/live-editor/initial.png": "57313599964308512499eb94d35d8711ff230ec6e8f4542802a147741efc77d4", + ".cache/blender-final-candidate/verified/live-editor/renamed.png": "7df8f30b892f58af00ebfe48db3ddfb58150c1faffa19fa2fc792e3e65e8cfe1" + }, + "results": { + "blender": "Blender 4.5.3 LTS", + "asset_id": "54c039e4-c19f-4365-a9f8-a38416ec6e3f", + "initial_generation": "499f254ce440ecb86118f2f6803f21013e763d35cfdba1c4356be7a457eec718", + "renamed_generation": "797e63652213752dc7c92029c4e9743517c5f40547b97eac748e60b17f142926", + "stable_output_ids": true, + "removed_output_conflict": true, + "failed_import_keeps_generation": true, + "authoring_preserved": true, + "changed_source_marked_stale": true, + "invalid_bundle_marked_stale": true, + "live_editor": { + "conflict_keeps_visible_generation": true, + "instances": 2, + "live_geometry_updated": true, + "opaque_gameplay_preserved": true, + "physics_fields_preserved": true, + "placement_and_tint_preserved": true, + "status": "passed", + "validation_enabled": true, + "validation_errors": 0 + } + }, + "limits": [ + "Linux offscreen Vulkan Editor snapshot with validation; not a physical desktop input test.", + "No native OS IME or physical mixed-monitor DPI exercised.", + "Static geometry/material fixture; no skeleton/animation or arbitrary Blender shader fidelity claim.", + "Windows export and native platform acceptance are separate." + ], + "original_harness_failure": "Whole faset_assets response equality was invalidated by new freshness current->stale; immutable active manifests remain equal. Revised harness separately verifies stale bundle.changed/bundle.unavailable and last-good generation.", + "hardware_context": { + "host_gpu": "NVIDIA GeForce RTX 2080 Ti", + "nvidia_driver": "595.84" + }, + "screenshot": { + "path": ".cache/blender-final-candidate/verified/live-editor/renamed.png", + "stage": "After successful stable-ID rename and geometry reimport, before removed-output conflict.", + "conversion": "PNG encoded from renderer-produced renamed.ppm without resizing, cropping, recoloring or content changes." + } +} diff --git a/docs/validation/checkpoint5-linux-2026-09-18/editor-blender.png b/docs/validation/checkpoint5-linux-2026-09-18/editor-blender.png new file mode 100644 index 0000000000000000000000000000000000000000..3da5d785ef1ad6143e23035cd1077437caaa8b88 GIT binary patch literal 93699 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

IKo3JD>Q2%~bU)P-$+)^Un#LciR85#jk}_t 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 literal 0 HcmV?d00001 diff --git a/docs/validation/checkpoint5-linux-2026-09-18/first-run.json b/docs/validation/checkpoint5-linux-2026-09-18/first-run.json new file mode 100644 index 0000000..4a9590a --- /dev/null +++ b/docs/validation/checkpoint5-linux-2026-09-18/first-run.json @@ -0,0 +1,20 @@ +{ + "source_commit": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "recorded_at_utc": "2026-09-18T02:50:10.534440+00:00", + "platform": "Linux-7.0.0-31-generic-x86_64-with-glibc2.43", + "source": "Fresh offline git archive checkout, default options, no previous build directory", + "command": "xvfb-run -a /build/linux-debug/faset_editor --project /MyGame --new MyGame --dimension 3 --frames 3 --capture /editor.ppm", + "editor_sha256": "71182902617d617749d383ff87d29929013751b9597dcdbcb5f25ce323ebb64b", + "checks": { + "exit_code": 0, + "project_name": "MyGame", + "dimension": 3, + "gameplay_files_match_templates": true, + "rendered_capture": [ + 1440, + 900 + ], + "capture_sha256": "e15c1e50106a4a188c817e9acb6382f13f094f163768a937ae39ddee3637971b" + }, + "scope": "Manual build prerequisites already installed; engine built from clean source. Three native X11/Xvfb Editor frames; GPU execution does not prove physical OS IME or mixed-monitor behavior." +} diff --git a/docs/validation/checkpoint5-linux-2026-09-18/offline.json b/docs/validation/checkpoint5-linux-2026-09-18/offline.json new file mode 100644 index 0000000..c19a7e3 --- /dev/null +++ b/docs/validation/checkpoint5-linux-2026-09-18/offline.json @@ -0,0 +1,12 @@ +{ + "format": "faset.offline-build", + "version": 1, + "revision": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "recorded_at_utc": "2026-09-18T02:47:01.536454+00:00", + "seconds": 205.76940188600202, + "returncode": 0, + "network": "fresh user/network namespace; external connection must fail", + "source": "git archive of committed HEAD; no existing build directory", + "inputs": "prefetched archives + Slang; system compiler, SDK and development libraries", + "tests": "full native build, CPU CTests; GPU/window execution verified separately" +} diff --git a/docs/validation/checkpoint5-linux-2026-09-18/playable-exports.json b/docs/validation/checkpoint5-linux-2026-09-18/playable-exports.json new file mode 100644 index 0000000..308cdb1 --- /dev/null +++ b/docs/validation/checkpoint5-linux-2026-09-18/playable-exports.json @@ -0,0 +1,452 @@ +{ + "format": "faset.playable-export-verification", + "version": 1, + "started_utc": "2026-09-18T02:43:37.045153+00:00", + "platform": "linux", + "engine": "/home/emil/Desktop/Faset_Engine", + "editor": "/home/emil/Desktop/Faset_Engine/build/linux-debug/faset_editor", + "standalone_root": "/tmp/faset-playable-exports-25rumkwv", + "frames_per_game": 120, + "status": "passed", + "projects": [ + { + "name": "collect-2d", + "dimension": 2, + "source_inputs": [ + { + "path": ".gitignore", + "sha256": "53aa4d7124d4c8d93b8d7c4cb470b6c82b844e019a660d6243ffeadc50602de1" + }, + { + "path": "README.md", + "sha256": "d6c35206b10ee28efd5cc02da77a3ce9b6e1f8ce085793150bbf2f23bd925d65" + }, + { + "path": "Scenes/main.scene.json", + "sha256": "be48a6d21385e717b6c8ebf9a3b4322eebbeff6bb067428e089ab5214be79854" + }, + { + "path": "Scripts/Gameplay.cpp", + "sha256": "6b581b24da613e1915819de0a0f0ea820dd3333efa62c4b479a9ea7ffdbc6bcc" + }, + { + "path": "Scripts/Gameplay.hpp", + "sha256": "4121587182ed4f74901b1c0757c53daadbf309af1d6bb5dd87fef142130ccbbe" + }, + { + "path": "project.faset.json", + "sha256": "36e2acd32210f5489372a2331346a3294ead2395f0713e3e5fd2da4e83263689" + } + ], + "generation": "2bf52972-98ee-4379-8b7c-eb6f0dfca6a5", + "configuration": "Release", + "standalone_directory": "/tmp/faset-playable-exports-25rumkwv/Faset Café 世界/collect-2d", + "executable": "faset_player", + "package_file_count": 17, + "asset_generations": {}, + "device": "NVIDIA GeForce RTX 2080 Ti", + "validation_enabled": true, + "validation_errors": 0, + "completed_frames": 120, + "summary_ms": { + "gpu": { + "max": 0.592896, + "min": 0.558112, + "p50": 0.55904, + "p95": 0.577952, + "samples": 120 + }, + "render_call": { + "max": 4.604147, + "min": 1.474391, + "p50": 1.713513, + "p95": 2.008409, + "samples": 120 + }, + "renderer_cpu": { + "max": 4.60056, + "min": 1.472197, + "p50": 1.710667, + "p95": 2.00375, + "samples": 120 + }, + "renderer_readback_cpu": { + "max": 2.634671, + "min": 0.395376, + "p50": 0.449368, + "p95": 0.596425, + "samples": 120 + }, + "simulation": { + "max": 0.394364, + "min": 0.133442, + "p50": 0.146897, + "p95": 0.212962, + "samples": 120 + }, + "snapshot": { + "max": 0.208042, + "min": 0.125827, + "p50": 0.134384, + "p95": 0.182785, + "samples": 120 + }, + "wall": { + "max": 5.039538, + "min": 1.763687, + "p50": 2.01393, + "p95": 2.375862, + "samples": 120 + } + }, + "capture": { + "width": 1280, + "height": 720, + "sampled_colors": 7, + "sha256": "f9d74067d87c1ebf30c6db372a26d93b7285791c8cb0307a24af5adb23bf201a" + }, + "source_project_paths_unavailable": true, + "status": "passed" + }, + { + "name": "collect-3d", + "dimension": 3, + "source_inputs": [ + { + "path": ".gitignore", + "sha256": "53aa4d7124d4c8d93b8d7c4cb470b6c82b844e019a660d6243ffeadc50602de1" + }, + { + "path": "Assets/exit-arch/create.py", + "sha256": "9c7291124fb1498a3f992ef74afc39cd98f9cc3e2ff4687c633bece85d580905" + }, + { + "path": "Assets/exit-arch/manifest.json", + "sha256": "7a13347adc89761fb02ebeab3c58ba87f319cd62f53ec87a3bb61bcd066ffa55" + }, + { + "path": "Assets/exit-arch/manifest.json.faset-import.json", + "sha256": "ef4eef28a18a0cc40a40df75db34c97faa5d783b891265f862633f3aec514afc" + }, + { + "path": "Assets/exit-arch/payload/ee6c748a212905fc9c465cbe95c033e94b80be643624f4f240ad5353761205b3.glb", + "sha256": "ee6c748a212905fc9c465cbe95c033e94b80be643624f4f240ad5353761205b3" + }, + { + "path": "Assets/exit-arch/source.blend", + "sha256": "ff2676bec97ab778e531e87a185ad716049ea3b05b4a4f0027a6345737735035" + }, + { + "path": "README.md", + "sha256": "2df5ca4df47ab5d3d05d41508201296d4ad5ec876245f19f061767ade6a30e8f" + }, + { + "path": "Scenes/main.scene.json", + "sha256": "61b65baf79e52f07ac386a7f98acf985a1b09e88770cdde37ef461dc94b9dcd7" + }, + { + "path": "Scripts/Extensions/Beacon.hpp", + "sha256": "c60d56161eead16f541835053ad32ac3333383d83015709c060b7d45e0af5a97" + }, + { + "path": "Scripts/Gameplay.cpp", + "sha256": "5d419786a6861d7f14ed4eca54634694d8cd43be5dbbd60a2c6790db665c3a6a" + }, + { + "path": "Scripts/Gameplay.hpp", + "sha256": "4121587182ed4f74901b1c0757c53daadbf309af1d6bb5dd87fef142130ccbbe" + }, + { + "path": "project.faset.json", + "sha256": "43d273b1054ec9069369073f54cc1fc7efb3d6b699dcd342c552a2dc2ef852ef" + } + ], + "import": { + "asset_id": "5832763b-3ed0-44d6-9088-0b524f196a91", + "cache_hit": false, + "diagnostics": [], + "generation": "4a5bfcc1437a7b4d65c388da2beace0308cceda8e16503d08ce676260f21d5c2", + "manifest": { + "asset_id": "5832763b-3ed0-44d6-9088-0b524f196a91", + "files": [ + { + "path": "meshes/mesh-fc069a07a87e0f831fd92efdbc4efa1f-0.fmesh", + "sha256": "98b38aca7d2786c58a2265ba4d1f2255123f03d31d947e239232cf55bfdc2c97", + "size": 928 + }, + { + "path": "meshes/mesh-0961e2a3f92977836a3aa80cab09277e-0.fmesh", + "sha256": "98b38aca7d2786c58a2265ba4d1f2255123f03d31d947e239232cf55bfdc2c97", + "size": 928 + }, + { + "path": "meshes/mesh-93b5b3e52cb6bb120fefb80e2d8e222b-0.fmesh", + "sha256": "3875c16ea628d4126373ebfe07816621fbad3961596a2fcd112f1eec0e378071", + "size": 928 + } + ], + "generation": "4a5bfcc1437a7b4d65c388da2beace0308cceda8e16503d08ce676260f21d5c2", + "importer": "faset-gltf-1/cgltf-1.15", + "input_key": { + "bundle_sha256": "7a13347adc89761fb02ebeab3c58ba87f319cd62f53ec87a3bb61bcd066ffa55", + "dependencies": {}, + "importer": "faset-gltf-1/cgltf-1.15", + "settings": {}, + "source": "ee6c748a212905fc9c465cbe95c033e94b80be643624f4f240ad5353761205b3", + "target_profile": "desktop-static-pbr-v1", + "toolchain": { + "cgltf": "360db1a95480fe102ae9c69b27c5d101167ff5ba", + "stb": "2c980bb59875b0d32144a71867fbdebb2f77cd20" + } + }, + "kind": "scene", + "materials": [ + { + "alpha_cutoff": 0.5, + "alpha_mode": "OPAQUE", + "base_color": [ + 0.2199999988079071, + 0.28999999165534973, + 0.3400000035762787, + 1.0 + ], + "base_color_texture": -1, + "double_sided": true, + "emissive": [ + 0.0, + 0.0, + 0.0 + ], + "emissive_texture": -1, + "format": "faset.material", + "id": "material-bb63b094febbf90bb7049c9bcf7587bc", + "metallic": 0.07999999821186066, + "metallic_roughness_texture": -1, + "name": "Weathered stone", + "normal_texture": -1, + "occlusion_texture": -1, + "roughness": 0.800000011920929, + "unlit": false, + "version": 1 + } + ], + "meshes": [ + { + "id": "mesh-fc069a07a87e0f831fd92efdbc4efa1f", + "name": "Cube.001", + "primitives": [ + { + "material": 0, + "path": "meshes/mesh-fc069a07a87e0f831fd92efdbc4efa1f-0.fmesh" + } + ] + }, + { + "id": "mesh-0961e2a3f92977836a3aa80cab09277e", + "name": "Cube.002", + "primitives": [ + { + "material": 0, + "path": "meshes/mesh-0961e2a3f92977836a3aa80cab09277e-0.fmesh" + } + ] + }, + { + "id": "mesh-93b5b3e52cb6bb120fefb80e2d8e222b", + "name": "Cube.003", + "primitives": [ + { + "material": 0, + "path": "meshes/mesh-93b5b3e52cb6bb120fefb80e2d8e222b-0.fmesh" + } + ] + } + ], + "nodes": [ + { + "id": "node-cec6e8cea80cf5a98ba3263a0156bee4", + "local_transform": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.4500000476837158, + 1.2999999523162842, + 1.0 + ], + "mesh": 0, + "name": "Left post", + "parent_id": "", + "stable_source_id": true + }, + { + "id": "node-5f8a90a6c4d27020d20bd4fa81d1d958", + "local_transform": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.4500000476837158, + -1.2999999523162842, + 1.0 + ], + "mesh": 1, + "name": "Right post", + "parent_id": "", + "stable_source_id": true + }, + { + "id": "node-482463e486a8d1fd741340c653cd128f", + "local_transform": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 3.0, + 0.0, + 1.0 + ], + "mesh": 2, + "name": "Lintel", + "parent_id": "", + "stable_source_id": true + } + ], + "outputs": [ + "node-cec6e8cea80cf5a98ba3263a0156bee4", + "node-5f8a90a6c4d27020d20bd4fa81d1d958", + "node-482463e486a8d1fd741340c653cd128f", + "mesh-fc069a07a87e0f831fd92efdbc4efa1f", + "mesh-0961e2a3f92977836a3aa80cab09277e", + "mesh-93b5b3e52cb6bb120fefb80e2d8e222b", + "material-bb63b094febbf90bb7049c9bcf7587bc" + ], + "payload_source": "/home/emil/Desktop/Faset_Engine/.cache/playable-final-candidate/Faset Café 世界/collect-3d/Assets/exit-arch/payload/ee6c748a212905fc9c465cbe95c033e94b80be643624f4f240ad5353761205b3.glb", + "schema_version": 1, + "settings": {}, + "source": "/home/emil/Desktop/Faset_Engine/.cache/playable-final-candidate/Faset Café 世界/collect-3d/Assets/exit-arch/manifest.json", + "source_sha256": "ee6c748a212905fc9c465cbe95c033e94b80be643624f4f240ad5353761205b3", + "textures": [] + }, + "previous_generation": "", + "removed_output_ids": [] + }, + "generation": "b0a40126-7d6c-414f-843a-23a677ad6f8f", + "configuration": "Release", + "standalone_directory": "/tmp/faset-playable-exports-25rumkwv/Faset Café 世界/collect-3d", + "executable": "faset_player", + "package_file_count": 22, + "asset_generations": { + "5832763b-3ed0-44d6-9088-0b524f196a91": "4a5bfcc1437a7b4d65c388da2beace0308cceda8e16503d08ce676260f21d5c2" + }, + "device": "NVIDIA GeForce RTX 2080 Ti", + "validation_enabled": true, + "validation_errors": 0, + "completed_frames": 120, + "summary_ms": { + "gpu": { + "max": 0.615584, + "min": 0.578944, + "p50": 0.58096, + "p95": 0.597312, + "samples": 120 + }, + "render_call": { + "max": 4.88184, + "min": 1.554402, + "p50": 1.842957, + "p95": 2.153072, + "samples": 120 + }, + "renderer_cpu": { + "max": 4.876761, + "min": 1.552489, + "p50": 1.838648, + "p95": 2.149254, + "samples": 120 + }, + "renderer_readback_cpu": { + "max": 2.787459, + "min": 0.398262, + "p50": 0.483592, + "p95": 0.577219, + "samples": 120 + }, + "simulation": { + "max": 0.38139, + "min": 0.16457, + "p50": 0.192834, + "p95": 0.278605, + "samples": 120 + }, + "snapshot": { + "max": 0.816561, + "min": 0.155042, + "p50": 0.172175, + "p95": 0.243289, + "samples": 120 + }, + "wall": { + "max": 6.073458, + "min": 1.902519, + "p50": 2.248081, + "p95": 2.61298, + "samples": 120 + } + }, + "capture": { + "width": 1280, + "height": 720, + "sampled_colors": 42, + "sha256": "914060401de93d08b33bf09a1e210859e491a0060c331e5e2044f517356f4980" + }, + "source_project_paths_unavailable": true, + "status": "passed" + } + ], + "finished_utc": "2026-09-18T02:47:27.268249+00:00", + "engine_commit": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "editor_sha256": "04baf2ec09f3f097657553d5b862ba5c90749d2cdbf4a239c6b92a45dee56ee9", + "working_tree_clean_at_start": true, + "engine_commit_at_completion": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "timings_are_benchmark_evidence": false, + "timing_note": "Functional export/relocation/validation/profile completeness acceptance; timings are not benchmark evidence because a separate build runs concurrently.", + "visual_inspection": { + "status": "passed", + "notes": [ + "2D capture shows player, platforms, three collectibles, barrier and exit marker.", + "3D capture shows player, room, collectibles, platform, gate and imported exit structure without missing geometry." + ], + "png_previews": [ + "evidence/collect-2d.png", + "evidence/collect-3d.png" + ] + } +} diff --git a/docs/validation/checkpoint5-linux-2026-09-18/recovery.json b/docs/validation/checkpoint5-linux-2026-09-18/recovery.json new file mode 100644 index 0000000..9a2b7a0 --- /dev/null +++ b/docs/validation/checkpoint5-linux-2026-09-18/recovery.json @@ -0,0 +1,36 @@ +{ + "format": "faset.editor-recovery-verification", + "version": 1, + "status": "passed", + "checks": [ + "acknowledged unsaved edit survives killed Editor and explicit recovery", + "failed save preserves live authoring and last saved file", + "failed Player process is reported without changing authoring data", + "Undo repairs the scene, then Play and Stop leave authoring unchanged", + "failed C++ build keeps prior schema and reports stale status", + "fixed C++ rebuild restores current schema status", + "failed import preserves authoring" + ], + "recorded_at_utc": "2026-09-18T02:50:13.733355+00:00", + "platform": "Linux-7.0.0-31-generic-x86_64-with-glibc2.43", + "editor_sha256": "04baf2ec09f3f097657553d5b862ba5c90749d2cdbf4a239c6b92a45dee56ee9", + "logs": [ + "build completed", + "Gameplay schema loaded", + "Play started in a separate Player process", + "Player failed: unsupported builtin component version: faset.transform\n", + "Player exited with code 1", + "build completed", + "Gameplay schema loaded", + "Play started in a separate Player process", + "New round: collect the three gold cubes, then reach the green exit. E resets.\n{\"device\":\"NVIDIA GeForce RTX 2080 Ti\",\"dimension\":2,\"frames\":411,\"ticks\":104,\"validation_errors\":0}\n", + "Play stopped; authoring scene unchanged", + "build failed: Process exited with code 1; see job log; previous gameplay metadata remains available and is marked stale", + "build completed", + "Gameplay schema loaded", + "Asset import failed: Cannot read: /home/emil/Desktop/Faset_Engine/.cache/editor-recovery-final-candidate/project/Assets/missing.glb\n" + ], + "engine_commit": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "engine_commit_at_completion": "0f34b036313c011861dbfd5828ed45c4f7940b05", + "source_note": "Completed before pending moved-input freshness changes to asset_data.cpp/asset_pipeline.cpp; final-candidate committed engine source." +} diff --git a/src/assets/asset_data.cpp b/src/assets/asset_data.cpp index 686dca0..0bde60d 100644 --- a/src/assets/asset_data.cpp +++ b/src/assets/asset_data.cpp @@ -133,7 +133,27 @@ Json AssetStore::current_manifest(const std::string& id) const { if (manifest.at("asset_id").get() != id || manifest.at("generation").get() != generation) throw std::runtime_error("Cooked manifest identity does not match its generation"); - // One pointer snapshot prevents mixing two concurrently published generations. + // Locations belong to the active pointer, not the immutable content recipe: + // moving sources can reuse an existing generation whose paths are historical. + // One pointer snapshot keeps logical source and payload in the same publication. + if (pointer.contains("payload_source")) + manifest["payload_source"] = pointer.at("payload_source"); + else { + // Earlier pointers only stored source. Ordinary files are their own + // payload; a bundle payload keeps its path relative to the manifest. + const auto previous_source = + faset::path_from_utf8(manifest.at("source").get()); + const auto previous_payload = faset::path_from_utf8( + manifest.value("payload_source", faset::path_to_utf8(previous_source))); + const auto current_source = faset::path_from_utf8(pointer.at("source").get()); + const auto current_payload = + previous_payload == previous_source + ? current_source + : (current_source.parent_path() / + previous_payload.lexically_relative(previous_source.parent_path())) + .lexically_normal(); + manifest["payload_source"] = faset::path_to_utf8(current_payload); + } manifest["source"] = pointer.at("source"); return manifest; } diff --git a/src/assets/asset_pipeline.cpp b/src/assets/asset_pipeline.cpp index 02476c9..3c4a8c6 100644 --- a/src/assets/asset_pipeline.cpp +++ b/src/assets/asset_pipeline.cpp @@ -828,7 +828,8 @@ ImportResult AssetPipeline::import_asset(const ImportRequest& request, ImportJob throw Cancelled{}; atomic_json(asset_root / "current.json", {{"schema_version", 1}, {"generation", result.generation}, - {"source", faset::path_to_utf8(logical_source)}}); + {"source", faset::path_to_utf8(logical_source)}, + {"payload_source", faset::path_to_utf8(source)}}); result.status = ImportStatus::succeeded; job.report(1, "complete"); } catch (const Cancelled&) { diff --git a/tests/assets_pipeline.cpp b/tests/assets_pipeline.cpp index ba4fbf5..2da2c31 100644 --- a/tests/assets_pipeline.cpp +++ b/tests/assets_pipeline.cpp @@ -101,6 +101,95 @@ void success(const ImportResult& result) { throw std::runtime_error(text); } } +void relocated_inputs(AssetPipeline& pipeline, const fs::path& root) { + const auto before = root / faset::path_from_utf8("Источники до переноса"); + const auto after = root / faset::path_from_utf8("Источники после переноса"); + fs::create_directories(before / "payload/nested"); + glb(before / "payload/nested/mesh.glb", "Moved bundle"); + const Json bundle{ + {"schema_version", 1}, + {"asset_id", "relocated-bundle"}, + {"files", + Json::array({{{"path", "payload/nested/mesh.glb"}, + {"sha256", faset::sha256_file(before / "payload/nested/mesh.glb")}}})}}; + save(before / "manifest.json", bundle.dump()); + const auto first = pipeline.import_asset({before / "manifest.json"}); + success(first); + const auto immutable = pipeline.generation_directory(first.asset_id) / "manifest.json"; + const auto original_digest = faset::sha256_file(immutable); + fs::rename(before, after); + const auto moved = pipeline.import_asset({after / "manifest.json"}); + success(moved); + require(moved.cache_hit && moved.generation == first.generation && + moved.asset_id == first.asset_id, + "Bundle directory relocation must reuse the content generation and identity"); + require(pipeline.freshness(first.asset_id).at("state") == "current" && + pipeline.current_manifest(first.asset_id).at("payload_source") == + faset::path_to_utf8(after / "payload/nested/mesh.glb"), + "Moved bundle freshness must inspect the current payload location"); + require(faset::sha256_file(immutable) == original_digest, + "Relocation must not rewrite an immutable generation manifest"); + // Earlier pointers only recorded logical source; they must remain readable + // after a cache-hit relocation without consulting missing old source files. + const auto pointer = pipeline.cache_root() / "assets" / first.asset_id / "current.json"; + auto old_pointer = faset::read_json(pointer); + old_pointer.erase("payload_source"); + faset::atomic_write_json(pointer, old_pointer); + require(pipeline.freshness(first.asset_id).at("state") == "current" && + pipeline.load_asset(first.asset_id).generation == first.generation, + "Legacy bundle pointer must resolve payload relative to the moved source"); + glb(after / "payload/nested/mesh.glb", "Moved bundle", true, false, .8f); + require(pipeline.freshness(first.asset_id).at("state") == "stale" && + pipeline.load_asset(first.asset_id).generation == first.generation, + "Changed relocated bundle payload must be stale without changing cooked data"); + + const auto external_before = root / "external-before"; + const auto external_after = root / faset::path_from_utf8("Внешний glTF после переноса"); + fs::create_directories(external_before / "deps"); + auto gltf = document("Moved external", true, false); + gltf["buffers"][0]["uri"] = "deps/geometry.bin"; + gltf["images"] = Json::array({{{"uri", "deps/picture.png"}, {"mimeType", "image/png"}}}); + gltf["textures"] = Json::array({{{"source", 0}}}); + gltf["materials"][0]["pbrMetallicRoughness"]["baseColorTexture"] = {{"index", 0}}; + save(external_before / "model.gltf", gltf.dump()); + save(external_before / "deps/geometry.bin", geometry(0)); + save(external_before / "deps/picture.png", faset::test_images::png_red_green); + const auto external = pipeline.import_asset({external_before / "model.gltf"}); + success(external); + const auto external_immutable = + pipeline.generation_directory(external.asset_id) / "manifest.json"; + const auto external_digest = faset::sha256_file(external_immutable); + fs::rename(external_before, external_after); + const auto relocated = pipeline.import_asset({external_after / "model.gltf"}); + success(relocated); + require(relocated.cache_hit && relocated.generation == external.generation && + relocated.asset_id == external.asset_id && + pipeline.freshness(external.asset_id).at("state") == "current", + "Moving glTF and external dependencies together must preserve a current cache hit"); + const auto external_pointer = + pipeline.cache_root() / "assets" / external.asset_id / "current.json"; + auto legacy = faset::read_json(external_pointer); + legacy.erase("payload_source"); + faset::atomic_write_json(external_pointer, legacy); + require(pipeline.freshness(external.asset_id).at("state") == "current", + "Legacy ordinary glTF pointer must treat the current source as payload"); + save(external_after / "deps/geometry.bin", geometry(.4f)); + auto freshness = pipeline.freshness(external.asset_id); + require(freshness.at("state") == "stale" && + freshness.at("reasons")[0].at("code") == "dependency.changed" && + freshness.at("reasons")[0].at("path") == + faset::path_to_utf8((external_after / "deps/geometry.bin").lexically_normal()), + "Freshness must hash external geometry at its new location after directory move"); + save(external_after / "deps/geometry.bin", geometry(0)); + save(external_after / "deps/picture.png", faset::test_images::png_blue_white); + freshness = pipeline.freshness(external.asset_id); + require(freshness.at("state") == "stale" && + freshness.at("reasons")[0].at("path") == + faset::path_to_utf8((external_after / "deps/picture.png").lexically_normal()), + "Freshness must inspect moved external images too"); + require(faset::sha256_file(external_immutable) == external_digest, + "Freshness/relocation must leave the published glTF generation immutable"); +} } // namespace int main() { const auto root = @@ -410,6 +499,16 @@ int main() { "Image rename retained old source pointer"); require(pipeline.overrides(image_first.asset_id).contains(texture_id), "Image rename/reimport lost overrides"); + require(pipeline.freshness(image_first.asset_id).at("state") == "current", + "Renamed and reimported PNG must be current, not stale at its old payload path"); + const auto image_pointer = + pipeline.cache_root() / "assets" / image_first.asset_id / "current.json"; + auto old_image_pointer = faset::read_json(image_pointer); + old_image_pointer.erase("payload_source"); + faset::atomic_write_json(image_pointer, old_image_pointer); + require(pipeline.freshness(image_first.asset_id).at("state") == "current", + "Legacy image pointer must follow the renamed logical source"); + relocated_inputs(pipeline, root); const auto jpeg = root / "sprite.JPEG"; save(jpeg, faset::test_images::jpeg_red_green); auto jpeg_import = pipeline.import_asset({jpeg}); diff --git a/tools/verify_blender_roundtrip.py b/tools/verify_blender_roundtrip.py index 8c9e817..e7dc877 100644 --- a/tools/verify_blender_roundtrip.py +++ b/tools/verify_blender_roundtrip.py @@ -50,20 +50,35 @@ def verify(blender, editor, output, ui_probe=None): def ids(value): return sorted(value["outputs"]) assert ids(manifest) == ids(renamed["result"]["manifest"]), (manifest, renamed) + def active_manifests(assets): + return {item["id"]: item["manifest"] for item in assets["assets"]} + def freshness(assets): + return next(item["freshness"] for item in assets["assets"] if item["id"] == identity) active = command("faset_assets", {}) + assert freshness(active)["state"] == "current", active removed = import_stage("removed") assert removed["state"] == "conflict", removed - assert command("faset_assets", {}) == active + after_removal = command("faset_assets", {}) + # Source freshness must change without replacing the last-good generation. + assert active_manifests(after_removal) == active_manifests(active) + assert freshness(after_removal)["state"] == "stale", after_removal + assert any(reason["code"] == "bundle.changed" + for reason in freshness(after_removal)["reasons"]), after_removal (source / "manifest.json").write_text('{"broken":true}') failed = command("faset_import", {"path": "Assets/door/manifest.json"}) assert failed["state"] == "failed", failed - assert command("faset_assets", {}) == active + after_failure = command("faset_assets", {}) + assert active_manifests(after_failure) == active_manifests(active) + assert freshness(after_failure)["state"] == "stale", after_failure + assert any(reason["code"] == "bundle.unavailable" + for reason in freshness(after_failure)["reasons"]), after_failure assert scene_file.read_bytes() == before report = {"blender": subprocess.check_output([str(blender), "--version"], text=True).splitlines()[0], "asset_id": identity, "initial_generation": first["result"]["generation"], "renamed_generation": renamed["result"]["generation"], "stable_output_ids": True, "removed_output_conflict": True, - "failed_import_keeps_generation": True, "authoring_preserved": True} + "failed_import_keeps_generation": True, "authoring_preserved": True, + "changed_source_marked_stale": True, "invalid_bundle_marked_stale": True} if ui_probe: probe = subprocess.run([str(ui_probe), str(fixture), str(output / "live-editor")], capture_output=True, text=True, encoding="utf-8")