@woosh/meep-engine 3.60.4 → 3.60.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2621 -0
- package/CONTRIBUTING.md +7 -0
- package/MIGRATION_2_TO_3.md +136 -0
- package/SECURITY.md +4 -0
- package/editor/EditorCameraNavigation.d.ts +2 -1
- package/editor/EditorCameraNavigation.d.ts.map +1 -1
- package/editor/EditorCameraNavigation.js +51 -57
- package/package.json +6 -2
- package/src/engine/EngineHarness.d.ts +3 -9
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +6 -8
- package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
- package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
- package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
- package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
- package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
- package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
- package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
- package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
- package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/Water.js +8 -0
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem.js +731 -734
- package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
- package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
- package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
- package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
- package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
- package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
- package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
- package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
- package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
- package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
- package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
- package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
- package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
- package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
- package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
- package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
- package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/playground/water_refraction/README.md +5 -6
- package/src/shade/playground/water_refraction/index.html +1 -1
- package/src/shade/renderer/Renderer.d.ts +11 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +50 -6
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
- package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
- package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
- package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +28 -45
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +111 -119
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +22 -3
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +84 -3
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +7 -6
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +12 -11
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
- package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
- package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
- package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
- package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
- package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
- package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
- package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
- package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
- package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
- package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts +11 -1
- package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.js +16 -8
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +7 -3
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
- package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
- package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
- package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
- package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +22 -13
- package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water_avboit.js +7 -7
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
- package/editor/REWORK_PLAN_2026_08.md +0 -692
- package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
- package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
- package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
- package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
- package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
- package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
- package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
- package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
- package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
- package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
- package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
- package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
- package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
- package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
- package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
- package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
- package/src/REVIEW_2026_08_06/adv-core.md +0 -511
- package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
- package/src/REVIEW_2026_08_06/adv-view.md +0 -446
- package/src/REVIEW_2026_08_06/api-core.md +0 -296
- package/src/REVIEW_2026_08_06/api-engine.md +0 -195
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
- package/src/REVIEW_2026_08_06/core-binary.md +0 -348
- package/src/REVIEW_2026_08_06/core-collection.md +0 -376
- package/src/REVIEW_2026_08_06/core-math.md +0 -221
- package/src/REVIEW_2026_08_06/core-process.md +0 -266
- package/src/REVIEW_2026_08_06/core-science.md +0 -223
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
- package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
- package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
- package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
- package/src/REVIEW_2026_08_06/engine-input.md +0 -197
- package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
- package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
- package/src/REVIEW_2026_08_06/generation.md +0 -176
- package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
- package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
- package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
- package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
- package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
- package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
- package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
- package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
- package/src/REVIEW_2026_08_06/view.md +0 -667
- package/src/REVIEW_2026_08_06.md +0 -610
- package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
- package/src/core/bvh2/bvh3/NOTES.md +0 -2
- package/src/core/bvh8/NOTES.md +0 -63
- package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
- package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
- package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
- package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
- package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
- package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
- package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
- package/src/core/geom/3d/gjk/NOTES.md +0 -8
- package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
- package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
- package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
- package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
- package/src/engine/REVIEW_2026_07_03.md +0 -211
- package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
- package/src/engine/control/first-person/DESIGN.md +0 -658
- package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
- package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
- package/src/engine/control/first-person/TODO.md +0 -160
- package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
- package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
- package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
- package/src/engine/ecs/ik/PLAN.md +0 -179
- package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
- package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
- package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
- package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
- package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
- package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
- package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
- package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
- package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
- package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
- package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
- package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
- package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
- package/src/engine/network/CONGESTION_CONTROL.md +0 -87
- package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
- package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
- package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
- package/src/engine/physics/cloth/PLAN.md +0 -3056
- package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
- package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
- package/src/engine/physics/fluid/solver/NOTES.md +0 -46
- package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
- package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
- package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
- package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
- package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
- package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
- package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
- package/src/format/image/avif/DECISIONS.md +0 -545
- package/src/format/image/jpeg/NOTES.md +0 -3
- package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
- package/src/generation/COORDINATES.md +0 -54
- package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
- package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
- package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
- package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
- package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
- package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
- package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
- package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
- package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
- package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
- package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
- package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
- package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
- package/src/shade/RENDERER_CONTRACT.md +0 -697
- package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
- package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
- package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
- package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
- package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
- package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
- package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
- package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
- package/src/shade/renderer/NOTES.md +0 -78
- package/src/shade/renderer/PLAN.md +0 -8
- package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
- package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
- package/src/shade/renderer/atmosphere/NOTES.md +0 -6
- package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
- package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
- package/src/shade/renderer/camera/orbital/README.md +0 -81
- package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
- package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
- package/src/shade/renderer/camera/orbital/index.js +0 -9
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
- package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
- package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
- package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
- package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
- package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
- package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
- package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
- package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
- package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
- package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
- package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
- package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
- package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
- package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
- package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
- package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
- package/src/shade/renderer/hiz/NOTES.md +0 -3
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
- package/src/shade/renderer/light/cluster/NOTES.md +0 -11
- package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
- package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
- package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
- package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
- package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
- package/src/shade/renderer/particles/DESIGN.md +0 -1011
- package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
- package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
- package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
- package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
- package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
- package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
- package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
- package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
- package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
- package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
- package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
- package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
- package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
- package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
- package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
- package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
- package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
- package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
- package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
- package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
- package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
- package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
- package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
- package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
- package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
- package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
- package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
- package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
- package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
- package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
- package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
- package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
- package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
- package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
- package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
- package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
- package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
- package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
- package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
- package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
- package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
- package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
- package/src/shade/wgsl/NOTES.md +0 -4
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
# Predicate applicability in `MultiPredicateEvaluator`
|
|
2
|
-
|
|
3
|
-
**Status:** implemented — revised after adversarial review (§9) and after implementation (§10)
|
|
4
|
-
**Date:** 2026-08-22
|
|
5
|
-
**Area:** `core/model/reactive/evaluation`, `engine/ecs/dynamic_actions`
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Symptom
|
|
10
|
-
|
|
11
|
-
Entering combat produces a cascade of four errors:
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
CombatScene.js:886 Failed to build unit entity
|
|
15
|
-
Error: value 'scope.request' is not found is undefined
|
|
16
|
-
at ReactiveReference.evaluate (ReactiveReference.js:136)
|
|
17
|
-
at MultiPredicateEvaluator.__resolve_node
|
|
18
|
-
at MultiPredicateEvaluator.next
|
|
19
|
-
at DynamicActorSystem.matchRule
|
|
20
|
-
at DynamicActorSystem.match
|
|
21
|
-
|
|
22
|
-
CombatScene.js:906 Failed to initialize state management
|
|
23
|
-
Error: expected entity_id to be a number, instead was 'undefined'
|
|
24
|
-
at EntityComponentDataset.getComponent
|
|
25
|
-
at manageCombatState (UnitStateManager.js:410)
|
|
26
|
-
|
|
27
|
-
dispatchViaProxy.js:24 Failed to dispatch handler __handleUnitStateEntered
|
|
28
|
-
Error: expected entity_id to be a number, instead was 'undefined'
|
|
29
|
-
at HighlightCurrentTurnCellCombatProcess.processStateEntry
|
|
30
|
-
|
|
31
|
-
EntityManager.js:576 Failed during update of system 'DynamicActorSystem'
|
|
32
|
-
Error: value 'scope.Team.value' is not found is undefined
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Only the first is a cause. The rest are consequences.
|
|
36
|
-
|
|
37
|
-
## 2. Minimal repro
|
|
38
|
-
|
|
39
|
-
No combat, no ECS, no game code — four lines against the evaluator:
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
const ev = new MultiPredicateEvaluator(e => e.computeTreeSize());
|
|
43
|
-
ev.build([compileReactiveExpression('a > 5'), compileReactiveExpression('b < 3')]);
|
|
44
|
-
ev.initialize({ b: 1 }); // 'a' deliberately unbound
|
|
45
|
-
ev.next();
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Measured:
|
|
49
|
-
|
|
50
|
-
| build | result for `{ b: 1 }` |
|
|
51
|
-
|---|---|
|
|
52
|
-
| dev (asserts live) | **throws** `value 'scope.a' is not found`, zero matches |
|
|
53
|
-
| prod (`unassert` strips `assert.defined`) | `[ "( b < 3 )" ]` |
|
|
54
|
-
|
|
55
|
-
Two things follow. First, dev and prod compute **different rule sets** from the same database and
|
|
56
|
-
the same context. Second, one unbound name anywhere in the database destroys the *entire* match in
|
|
57
|
-
dev: `b < 3` is satisfiable and is never reached.
|
|
58
|
-
|
|
59
|
-
Production is behaving as intended here. Development is not.
|
|
60
|
-
|
|
61
|
-
## 3. Causal chain in combat
|
|
62
|
-
|
|
63
|
-
1. `Entity.build(ecd)` adds components one at a time. `addComponentToEntityByIndex` synchronously
|
|
64
|
-
runs observers and dispatches `ComponentAdded` per component
|
|
65
|
-
(`EntityComponentDataset.js:1196`).
|
|
66
|
-
2. `DynamicActor` is added at `UnitMaker.js:204`; `Team` is appended last at `CombatScene.js:878`.
|
|
67
|
-
The moment `DynamicActor` lands, `AbstractContextSystem` links a `Context` whose `link()`
|
|
68
|
-
registers an **any-event** listener.
|
|
69
|
-
3. Every subsequent `ComponentAdded` therefore runs a full rule match against an entity that does
|
|
70
|
-
not yet have `Team`.
|
|
71
|
-
4. `DynamicActorSystem.matchRule` walks the shared predicate database. Predicates referencing
|
|
72
|
-
`request` (never bound outside a `SendRequest` context) and `Team.value` (not bound on this
|
|
73
|
-
entity yet) reach `ReactiveReference.evaluate`, which **asserts**.
|
|
74
|
-
5. The throw escapes `Entity.build` mid-loop. The entity is left in the dataset with a **partial
|
|
75
|
-
component set**, permanently missing `Team` and everything after it — which is why error #4
|
|
76
|
-
keeps firing on that entity forever.
|
|
77
|
-
6. `CombatScene.handleUnitAdded` catches and continues with `entity === undefined`, then writes
|
|
78
|
-
that `undefined` into `cell.entity` for every occupied cell.
|
|
79
|
-
|
|
80
|
-
Step 6 is the open REVIEW_03 item *"`CombatCell.entity` is `undefined` for occupied cells"*. This
|
|
81
|
-
is its repro.
|
|
82
|
-
|
|
83
|
-
Note what is **not** wrong here. Incremental component addition is `EntityComponentDataset`'s
|
|
84
|
-
contract and `Entity` is syntactic sugar over it. A listener that expects a particular component
|
|
85
|
-
tuple to be present when some *other* component is added is the thing in error, and that
|
|
86
|
-
expectation belongs to the listener.
|
|
87
|
-
|
|
88
|
-
## 4. Root cause
|
|
89
|
-
|
|
90
|
-
Two callers disagree about what "this name is not bound in this scope" means.
|
|
91
|
-
|
|
92
|
-
`ReactiveReference.evaluate` (`ReactiveReference.js:136`) treats it as a programmer error:
|
|
93
|
-
|
|
94
|
-
```js
|
|
95
|
-
evaluate(scope) {
|
|
96
|
-
const v = scope[this.name];
|
|
97
|
-
assert.defined(v, `value 'scope.${this.name}' is not found`);
|
|
98
|
-
return v;
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Its only production caller, `MultiPredicateEvaluator.__resolve_node` (line 358), treats it as
|
|
103
|
-
routine:
|
|
104
|
-
|
|
105
|
-
```js
|
|
106
|
-
value = expression.evaluate(this.__context);
|
|
107
|
-
|
|
108
|
-
if (value === undefined) {
|
|
109
|
-
// value not found
|
|
110
|
-
this.__invalidate_node(node);
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
The evaluator's own field documentation agrees with the evaluator (line 126): *"Marks nodes that
|
|
116
|
-
have failed to be evaluated, typically due to unresolved references."*
|
|
117
|
-
|
|
118
|
-
The assert throws before the caller can ever observe the `undefined` it explicitly checks for.
|
|
119
|
-
**The `if (value === undefined)` branch is dead code in development.** It executes only in
|
|
120
|
-
production, where `rollup-plugin-unassert` (`rollup.config.js:41`) strips the assert.
|
|
121
|
-
|
|
122
|
-
That dev/prod divergence is why this defect resisted four instrumented runs: instrumentation runs
|
|
123
|
-
unstripped, so the assert masks the very path being observed.
|
|
124
|
-
|
|
125
|
-
### The contradiction was already noticed once
|
|
126
|
-
|
|
127
|
-
`MultiPredicateEvaluator.spec.js` carries this helper:
|
|
128
|
-
|
|
129
|
-
```js
|
|
130
|
-
/**
|
|
131
|
-
* Build a reference that, when evaluated against the test context, yields a defined value whose
|
|
132
|
-
* runtime type does NOT match the reference's declared dataType. Such a reference cannot be
|
|
133
|
-
* resolved by the evaluator (type mismatch -> invalidated) and therefore stands in for an
|
|
134
|
-
* "unresolvable operand" without relying on a missing scope value (which would trip an assert).
|
|
135
|
-
*/
|
|
136
|
-
function unresolvableNumberRef(name) { ... }
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
A previous author needed an unresolvable operand, found that the natural way to express one trips
|
|
140
|
-
the assert, and invented a type-mismatch stand-in rather than resolve the contradiction. The
|
|
141
|
-
conflict is documented in the test suite; it just was never fixed.
|
|
142
|
-
|
|
143
|
-
### Both failing names are legitimately unbound
|
|
144
|
-
|
|
145
|
-
- **`request`** — `data.json:83` and friends are *response* rules
|
|
146
|
-
(`"request == 'combat-turn-start.archetype.Fighter' && ..."`). `request` enters scope only via
|
|
147
|
-
`SendRequestActionDescription.execute` → `requestBestActors`. For a `ComponentAdded` event it is
|
|
148
|
-
correctly absent.
|
|
149
|
-
- **`Team.value`** — `EntityProxyScope.resolvePath` yields nothing when the entity has no `Team`.
|
|
150
|
-
True for context/non-unit `DynamicActor` entities, and for the unit itself mid-build.
|
|
151
|
-
|
|
152
|
-
### Evidence of intended semantics
|
|
153
|
-
|
|
154
|
-
`DynamicRuleDescriptionTable` gates on binding before evaluating, at `matchBest` (line 100) and
|
|
155
|
-
`match` (line 137):
|
|
156
|
-
|
|
157
|
-
```js
|
|
158
|
-
const references = rule.references;
|
|
159
|
-
|
|
160
|
-
for (let i = 0; i < n; i++) {
|
|
161
|
-
if (context[references[i].name] === undefined) {
|
|
162
|
-
continue main; // rule does not apply to this scope
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const isMatch = rule.condition.evaluate(context); // only now
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
and its spec pins it: `test("match skips rules when context is missing needed property for
|
|
170
|
-
evaluation", ...)`, `expect(table.matchBest({})).toBeUndefined()`.
|
|
171
|
-
|
|
172
|
-
This is evidence of *intent*, not settled precedent — neither `match` nor `matchBest` has a
|
|
173
|
-
production caller (see §9, F1). The load-bearing conclusion is narrower and does not depend on
|
|
174
|
-
them: **an expression must not be evaluated against a scope that does not bind its references**,
|
|
175
|
-
and the check belongs before `evaluate`, not inside it.
|
|
176
|
-
|
|
177
|
-
## 5. Proposal
|
|
178
|
-
|
|
179
|
-
### R1 — Gate on binding inside `__resolve_node`, before `evaluate`
|
|
180
|
-
|
|
181
|
-
Add a binding query to `ReactiveReference` and consult it first:
|
|
182
|
-
|
|
183
|
-
```js
|
|
184
|
-
// ReactiveReference
|
|
185
|
-
/**
|
|
186
|
-
* Whether this reference resolves to a value in the given scope. Asking is how a caller avoids
|
|
187
|
-
* evaluating an expression that does not apply to the scope it is being matched against;
|
|
188
|
-
* `evaluate` remains strict and treats an unbound name as a broken precondition.
|
|
189
|
-
* @param {Object} scope
|
|
190
|
-
* @returns {boolean}
|
|
191
|
-
*/
|
|
192
|
-
isBound(scope) {
|
|
193
|
-
return scope[this.name] !== undefined;
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
```js
|
|
198
|
-
// MultiPredicateEvaluator.__resolve_node
|
|
199
|
-
if (expression.isReference) {
|
|
200
|
-
|
|
201
|
-
if (!expression.isBound(this.__context)) {
|
|
202
|
-
// reference is not bound in this scope - the node cannot apply here
|
|
203
|
-
this.__invalidate_node(node);
|
|
204
|
-
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
value = expression.evaluate(this.__context);
|
|
209
|
-
// ... type check unchanged
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
`undefined` never enters the expression algebra, `ReactiveReference.evaluate` keeps its assert
|
|
214
|
-
verbatim, and the assert becomes meaningful: it now fires only when a caller evaluated without
|
|
215
|
-
gating, which is a real bug.
|
|
216
|
-
|
|
217
|
-
This is **exactly behaviour-preserving against production**. Production reaches
|
|
218
|
-
`__invalidate_node` precisely when `scope[name]` is `undefined`; so does R1, by construction — the
|
|
219
|
-
two read the same scope through the same accessor.
|
|
220
|
-
|
|
221
|
-
### R2 — *(withdrawn during implementation — see §10)*
|
|
222
|
-
|
|
223
|
-
An earlier revision routed the binding query through `name in scope` and therefore required `has`
|
|
224
|
-
traps on `DataScope`, `EntityProxyScope` and `make_blackboard_proxy`, none of which answer `in`
|
|
225
|
-
correctly today. Implementing R1 showed the traps are not merely extra work but actively wrong for
|
|
226
|
-
this purpose: `in` and `!== undefined` disagree on a frame that binds a name *to* `undefined`, and
|
|
227
|
-
on that input an `in`-based gate would let `evaluate` be called on an unbound name — reintroducing
|
|
228
|
-
the crash it exists to prevent. The value probe above has no such gap and needs no new protocol.
|
|
229
|
-
|
|
230
|
-
### R3 — Make the evaluator self-correcting across matches
|
|
231
|
-
|
|
232
|
-
`initialize()` resets `evaluated` and the cursor; only `finalize()` resets `invalidated`
|
|
233
|
-
(line 484), and `matchRule` calls it unguarded:
|
|
234
|
-
|
|
235
|
-
```js
|
|
236
|
-
evaluator.initialize(context);
|
|
237
|
-
for (;;) { const predicate = evaluator.next(); ... }
|
|
238
|
-
evaluator.finalize(); // skipped when next() throws
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
A throw escaping `next()` leaves stale invalidations that silently suppress later matches.
|
|
242
|
-
**Demonstrated**, not theorised:
|
|
243
|
-
|
|
244
|
-
```
|
|
245
|
-
run 1: type-mismatched 'bad' invalidates p1; a throwing scope getter aborts the loop
|
|
246
|
-
run 2: clean context { bad: 50, boom: 5 } - both predicates are TRUE
|
|
247
|
-
matches: ["( boom > 0 )"] <-- p1 silently dropped
|
|
248
|
-
control (after finalize() ran):
|
|
249
|
-
matches: ["( ( bad > 1 ) && ( bad < 100 ) )", "( boom > 0 )"]
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
Wrong answers, no error, persisting for the life of the evaluator. Fix: `initialize()` establishes
|
|
253
|
-
the complete starting state, resetting `invalidated` there rather than depending on `finalize()`
|
|
254
|
-
having run. `finalize()` keeps a real job — releasing the scope, which is commonly a proxy over a
|
|
255
|
-
live frame that a finished evaluator must not keep reachable.
|
|
256
|
-
|
|
257
|
-
No `try/finally` in `matchRule`. Once `initialize()` owns the reset there is nothing left for it to
|
|
258
|
-
protect: a skipped `finalize()` retains one scope reference until the next match and cannot corrupt
|
|
259
|
-
a result. Guarding that would be guarding a condition that can no longer happen.
|
|
260
|
-
|
|
261
|
-
This is independent of R1 and stands on its own — it is a silent-wrong-answer bug and a one-line
|
|
262
|
-
fix. R1 removes the throw that triggers it today; R3 removes the class.
|
|
263
|
-
|
|
264
|
-
### R4 — Stop `CombatScene.handleUnitAdded` laundering failures into a corrupt board
|
|
265
|
-
|
|
266
|
-
Three `try`/`catch` blocks each swallow and continue with the state the failure produced:
|
|
267
|
-
|
|
268
|
-
- around `makeUnit` — leaves `entityBuilder` `undefined`, so the next line throws anyway, with a
|
|
269
|
-
worse message than the error that was caught;
|
|
270
|
-
- around `entityBuilder.build(ecd)` — leaves `entity` `undefined`, which then reaches
|
|
271
|
-
`manageCombatState` **and** `cell.entity`;
|
|
272
|
-
- around `manageCombatState` — hides a genuine wiring failure.
|
|
273
|
-
|
|
274
|
-
All three go. `Signal.send4` already isolates a throwing handler (`Signal.js:615`), so the failure
|
|
275
|
-
is reported there and the rest of the dispatch survives; what changes is that `handleUnitAdded`
|
|
276
|
-
stops running on past the failure and writing a non-entity into the board. `cells.forEach` also
|
|
277
|
-
moves ahead of `manageCombatState`, so the cells are populated as soon as there is a real entity to
|
|
278
|
-
put in them.
|
|
279
|
-
|
|
280
|
-
This does not make a failed unit fail the *scene* — `Signal` catches by design and that design is
|
|
281
|
-
not this change's business. It makes a failed unit stop corrupting the board.
|
|
282
|
-
|
|
283
|
-
## 6. Tests
|
|
284
|
-
|
|
285
|
-
- `MultiPredicateEvaluator.spec.js` — the §2 repro as a test: a predicate over an unbound name does
|
|
286
|
-
not match and does not throw, while a co-resident satisfiable predicate still matches; the same
|
|
287
|
-
name matches in scopes that do bind it. Plus the R3 leak repro: an exception escaping `next()`
|
|
288
|
-
must not change the result of the following match.
|
|
289
|
-
- `ReactiveReference.spec.js` — `isBound` reports absence, treats `0`/`false`/`''` as bound, and
|
|
290
|
-
reads through a scope that resolves names dynamically rather than as own properties.
|
|
291
|
-
- `EntityProxyScope.spec.js` — a component path is unbound until the entity has the component, and
|
|
292
|
-
resolves through the component's getter once it does.
|
|
293
|
-
- `DataScope.spec.js` — the combat failure end to end: a rule database matched against a real
|
|
294
|
-
entity scope skips the rules the scope cannot supply, and picks up the `Team` rule the moment the
|
|
295
|
-
component lands.
|
|
296
|
-
|
|
297
|
-
No `evaluate`-throws-on-unbound test: `assert` is stripped from production builds, so a rejection
|
|
298
|
-
test would pin behaviour that does not exist in the shipped artifact.
|
|
299
|
-
|
|
300
|
-
The existing short-circuit suite in `MultiPredicateEvaluator.spec.js` must pass unchanged. It pins
|
|
301
|
-
node-level resolvability semantics, which R1 deliberately does not touch.
|
|
302
|
-
|
|
303
|
-
## 7. Deferred
|
|
304
|
-
|
|
305
|
-
- **Unifying `match` / `matchBest` with the evaluator.** They duplicate gate-then-evaluate and can
|
|
306
|
-
drift from it, but neither has a production caller and meep is a library, so they are public
|
|
307
|
-
surface to keep rather than churn. Worth doing as its own change with its own justification; it
|
|
308
|
-
is not part of fixing this crash.
|
|
309
|
-
- **Predicate-level applicability gating** (skip a whole predicate when any of its references is
|
|
310
|
-
unbound, per `rule.references`). See §9, F3: it changes matching semantics, is not needed for the
|
|
311
|
-
crash, and is behaviourally inert on the current rule data.
|
|
312
|
-
|
|
313
|
-
## 8. Explicitly rejected
|
|
314
|
-
|
|
315
|
-
- **Making `ReactiveReference.evaluate` return `undefined`.** This turns `undefined` into a value in
|
|
316
|
-
the expression algebra: every operator must learn to propagate it, every consumer must handle it,
|
|
317
|
-
and the assert dies. It opens the floodgate instead of containing the problem. R1 achieves the
|
|
318
|
-
same outcome with the containment intact — the question "is this bound?" is asked and answered
|
|
319
|
-
before evaluation, and never becomes a value.
|
|
320
|
-
- **Making `Entity.build` atomic w.r.t. observers.** `Entity` is syntactic sugar over
|
|
321
|
-
`EntityComponentDataset`; the incremental add flow is correct. Expecting a given component tuple
|
|
322
|
-
to be present when some other component is added is the error, and it belongs to the consumer that
|
|
323
|
-
expects it — not to `Entity`.
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
## 9. Adversarial review
|
|
328
|
-
|
|
329
|
-
Reviewed against three questions: is it principled, does it damage the engine, is it the simplest
|
|
330
|
-
thing that works. The proposal that entered review is preserved here as **P1–P4**; §5 is the
|
|
331
|
-
revised outcome.
|
|
332
|
-
|
|
333
|
-
> **P1** intern reference names, give each predicate a `BitSet` of required names, gate whole
|
|
334
|
-
> predicates in `next()` with a per-match probe cache.
|
|
335
|
-
> **P2** delete the `value === undefined` branch as unreachable.
|
|
336
|
-
> **P3** adopt `match`/`matchBest`'s all-references-must-be-bound semantics across the evaluator.
|
|
337
|
-
> **P4** collapse the three matching paths onto one.
|
|
338
|
-
|
|
339
|
-
### Is it a principled fix?
|
|
340
|
-
|
|
341
|
-
**F1 — "the contract already exists, twice" was an appeal to incumbency.** *Confirmed.* `match` and
|
|
342
|
-
`matchBest` have no production callers; grep finds only `DynamicRuleDescriptionTable.spec.js`. The
|
|
343
|
-
original draft leaned on them as settled precedent. They are evidence of *intent* — reinforced by
|
|
344
|
-
their specs — but they are not a shipped contract, and "be consistent with two unused functions"
|
|
345
|
-
cannot by itself justify changing a hot path. §4 now states this explicitly and rests the argument
|
|
346
|
-
on the narrower claim instead.
|
|
347
|
-
|
|
348
|
-
**F2 — stronger evidence existed and was missed.** The `unresolvableNumberRef` helper in
|
|
349
|
-
`MultiPredicateEvaluator.spec.js` documents a previous author hitting this exact contradiction and
|
|
350
|
-
routing around it. That is far better evidence than the incumbency argument. Added to §4.
|
|
351
|
-
|
|
352
|
-
**F3 — P3 was a semantics change dressed as a fix.** *Upheld; P3 dropped.* It is not required to
|
|
353
|
-
fix the crash. On the actual data it buys nothing: of 89 rule conditions only 5 contain `||`, all
|
|
354
|
-
of the shape `(count == 1 && Team.value == 0) || (count == 1 && Team.value == 1)`, where both
|
|
355
|
-
branches reference `Team.value` — so predicate-level gating and short-circuiting agree on every one
|
|
356
|
-
of them. It would have been a hard-to-reason-about behaviour change with no demonstrated benefit.
|
|
357
|
-
|
|
358
|
-
**Diagnosis survived.** The dev/prod divergence is now measured (§2) rather than argued, and the
|
|
359
|
-
repro is four lines with no game code in it.
|
|
360
|
-
|
|
361
|
-
### Does it damage the engine?
|
|
362
|
-
|
|
363
|
-
**F4 — P1 pushed consumer policy into a core class.** *Partly upheld.* Reference-set interning and
|
|
364
|
-
per-predicate requirement bitsets are `dynamic_actions` shaped, added permanently to
|
|
365
|
-
`core/model/reactive` for one consumer. R1's binding query is the general form of the same idea
|
|
366
|
-
("do not evaluate an expression against a scope that does not bind its references") and adds one
|
|
367
|
-
predicate-free method to `ReactiveReference`.
|
|
368
|
-
|
|
369
|
-
**F5 — P2 was the most damaging item in the document.** *Upheld; P2 dropped.* The
|
|
370
|
-
`value === undefined` branch is only unreachable if *every* caller gates. `MultiPredicateEvaluator`
|
|
371
|
-
is public library surface and its own spec calls `initialize()` with plain objects; deleting the
|
|
372
|
-
branch converts a handled condition into a crash for any ungated caller. R1 does not delete it — it
|
|
373
|
-
**re-roots** it, reaching the same `__invalidate_node` via a binding query instead of by inspecting
|
|
374
|
-
`evaluate`'s return value. Same handling, sound trigger.
|
|
375
|
-
|
|
376
|
-
**F6 — the proposal was not implementable as written.** *Upheld.* Every variant needs to ask a scope
|
|
377
|
-
whether a name is bound, and no scope can answer today. Measured: `'a' in dataScope.proxy` is
|
|
378
|
-
`false` for a bound name, `'stack' in dataScope.proxy` is `true`, `'contains' in blackboardProxy` is
|
|
379
|
-
`true`. P1 would have silently gated on the host object's shape. The review's conclusion — add the
|
|
380
|
-
missing `has` traps — was itself superseded during implementation: the right answer is not to use
|
|
381
|
-
`in` at all. See §10.
|
|
382
|
-
|
|
383
|
-
**F7 — P4 churned public API for no defect.** *Upheld; moved to §7 Deferred.*
|
|
384
|
-
|
|
385
|
-
### KISS
|
|
386
|
-
|
|
387
|
-
**F8 — a materially smaller fix does the same job.** *Upheld; this is now R1.* Gating at the node
|
|
388
|
-
level inside `__resolve_node` is roughly four lines and subsumes P1, P2 and P3:
|
|
389
|
-
|
|
390
|
-
| | P1–P4 | R1 |
|
|
391
|
-
|---|---|---|
|
|
392
|
-
| name interning + per-predicate bitsets | yes | no |
|
|
393
|
-
| per-match probe cache | yes | no |
|
|
394
|
-
| semantics decision required (F3) | yes | no |
|
|
395
|
-
| existing short-circuit tests | need re-reasoning | pass unchanged |
|
|
396
|
-
| `match` / `matchBest` touched | yes | no |
|
|
397
|
-
| behaviour vs production | changes it | provably identical |
|
|
398
|
-
|
|
399
|
-
The one thing P1 had that R1 does not is skipping a predicate's DAG walk wholesale. That is a
|
|
400
|
-
performance argument, and see F9.
|
|
401
|
-
|
|
402
|
-
**F9 — the probe cache was premature optimisation.** *Upheld; dropped.* 89 rules, 84 distinct
|
|
403
|
-
conditions, and the evaluator already memoises per node through `evaluated`/`invalidated`. No
|
|
404
|
-
measurement was offered for the claim that gating is "cheaper than today". Work, right, fast — in
|
|
405
|
-
that order.
|
|
406
|
-
|
|
407
|
-
### Net effect of the review
|
|
408
|
-
|
|
409
|
-
Four of the five original items were dropped or deferred. What survives is: gate before evaluating
|
|
410
|
-
(R1), make the scopes answerable (R2), stop the evaluator carrying state across matches (R3), stop
|
|
411
|
-
the combat scene laundering failures (R4). R3 gained a working repro during review and is
|
|
412
|
-
independent of the rest.
|
|
413
|
-
|
|
414
|
-
---
|
|
415
|
-
|
|
416
|
-
## 10. Implementation record
|
|
417
|
-
|
|
418
|
-
Landed as R1, R3 and R4. R2 was withdrawn.
|
|
419
|
-
|
|
420
|
-
**R2 did not survive contact with the code.** The review concluded that a binding query needs `has`
|
|
421
|
-
traps because `in` is broken on all three scope proxies. Writing R1 showed the premise was wrong:
|
|
422
|
-
`in` is the incorrect operator here regardless of whether the traps exist. `in` and `!== undefined`
|
|
423
|
-
diverge on a frame that binds a name *to* `undefined`, and on that input an `in`-based gate reports
|
|
424
|
-
"bound" and hands `evaluate` an unbound name — the exact crash the gate exists to prevent. The value
|
|
425
|
-
probe has no such gap, needs no new protocol, and matches what `DynamicRuleDescriptionTable`'s gate
|
|
426
|
-
has always done. Three files of new trap code went away and the fix got smaller.
|
|
427
|
-
|
|
428
|
-
The broken `in` behaviour on `DataScope`, `EntityProxyScope` and `make_blackboard_proxy` is real and
|
|
429
|
-
was measured, but nothing uses `in` on those proxies. Fixing it is speculative work on an unused
|
|
430
|
-
path and is deliberately not part of this change.
|
|
431
|
-
|
|
432
|
-
**No `try`/`finally` in `matchRule`.** Once `initialize()` establishes complete state, a skipped
|
|
433
|
-
`finalize()` cannot affect a result — see R3.
|
|
434
|
-
|
|
435
|
-
**Verified.** The two gate tests were written first and failed with the reported production error
|
|
436
|
-
(`value 'scope.a' is not found`) before R1 existed. The `DataScope` end-to-end test was confirmed
|
|
437
|
-
non-vacuous by neutralising `isBound` and observing the original failure return verbatim:
|
|
438
|
-
`value 'scope.request' is not found`. Full regression over `engine/ecs`, `core/model` and
|
|
439
|
-
`core/lang`: 130 files, 884 tests, green — including the pre-existing short-circuit suite, which R1
|
|
440
|
-
does not touch.
|
|
441
|
-
|
|
442
|
-
**Not done, still open:** the deferred items in §7, and the residual noted in §3 — rule matching
|
|
443
|
-
still runs on every `ComponentAdded` during `Entity.build`, several times per unit. That is wasted
|
|
444
|
-
work and it lets a rule fire against a partially-populated entity. R1 makes it harmless rather than
|
|
445
|
-
fatal; whether actors should be matching at all mid-build is a separate design question about
|
|
446
|
-
`DynamicActorSystem`, not about `Entity`.
|