WIP: Engine parity: 15 missing features (localization, accessibility, profiler, behaviour trees, rollback, streaming, weather, blend shapes, LOD...) #142

Draft
khyretos wants to merge 1 commit from khyretos/claude/engine-parity into main AGit
Owner

Requested by Kees

WIP: one big PR on purpose, to be split. Kees (10-27): build the missing GAPS.md features locally first, split into small PRs later. This is everything built so far in one commit on top of main (4da51cf0). Nothing here should merge as is: the WIP prefix keeps auto-merge off. Each feature below is independent enough to cut out; the files they share are docs/GAPS.md, docs/SCRIPTING.md, docs/README.md, mkdocs.yml, ROADMAP.md, engine/CMakeLists.txt, tests/CMakeLists.txt, ScriptModule.h/.cpp, ScriptBindings.cpp, ModelModule.h/.cpp and EngineSettings.

What it adds

GAPS row Feature Doc page
F84 Localization: JSON string tables, CLDR plurals (~70 languages), placeholders, fallback chain, locale numbers; kke::bidi (Arabic shaping, right-to-left); Noto Arabic and Hebrew fonts; Lua loc.*; engine menus in en, nl, de, fr, es, ar, he docs/LOCALIZATION.md
F85 Accessibility: subtitles (subtitles.*), colour-blind modes (matrix in the tone-map pass), UI scale, high contrast, reduced motion. Still 🟡: no real screen reader docs/ACCESSIBILITY.md
F67 Runtime profiler: every module call timed per frame (Performance panel), captures of zones from all threads plus GPU time and memory to a Perfetto trace docs/PROFILING.md
F68 Opt-in crash reports (kke::crash): signal or exception, backtrace, last log lines. Sending is off by default (privacy.sendCrashReports). POSIX only, so 🟡 docs/CRASH_REPORTS.md
F71 Behaviour trees: sequence, selector, reactive, parallel, random, decorators, typed blackboard; C++ or JSON; Lua bt.* docs/AI.md
F63 Animation compression: clips packed (about 8x smaller), smallest-three rotations, validated file format (in GAPS row)
F23 BVH mocap import: loadModel("x.bvh") gives a skeleton and a clip for the retargeter docs/PROCEDURAL_ANIMATION.md
F27 kke::Timeline: keyframe tracks, events, markers, spans, clips, nested timelines; Lua timeline.* docs/TIMELINE.md
F39 Adaptive music: layered stems that follow parameters, transitions on beat/bar/segment end, stingers; Lua music.* docs/AUDIO.md
F55 Rollback netcode (GGPO-style): input guessing, rollback and re-run, checksums, input delay docs/NETWORKING.md
F64, F78 Level and world streaming: cells around foci with hysteresis and a load budget, floating origin; Lua world.* docs/SCENES.md
F81 Weather and snow: blended types, random weather, wind, temperature, wetness, snow cover; Lua weather.* docs/MOODS.md
F25 Blend shapes and facial animation: glTF morph targets applied before skinning (GPU and CPU paths), kke::FaceRig (expressions, blinking, lip-sync), Lua face.* docs/FACIAL_ANIMATION.md
F62 Automatic LOD: up to three levels per big mesh part over its own vertices (meshoptimizer), picked by screen size with hysteresis; graphics.autoLod, graphics.lodBias, KKE_LOD=0 docs/LOD.md
F38 Only the GAPS row is corrected: HRTF already existed

Limits, stated plainly

  • F55 rollback is a library with tests, not tried in a real networked game.
  • F68 crash reports: POSIX only.
  • F85: no screen reader.
  • F81 weather drives particles and wind but there is no editor tool for it.
  • F64/F78 do not stream the terrain's own chunks yet.
  • The Arabic, Hebrew, German, French, Spanish and Dutch menu strings are drafts and need a native speaker.
  • F25: weights animated inside a clip, VRM presets and eye look are not there. F62: no cross-fades or impostors.

Tests

  • kke_tests: all pass (1649 in the last full run).
  • python3 -W error tools/ci/check_{dependencies,game_modules,std_includes}.py pass.
  • tools/docs_site/run_recipes.py (39 ok, play skipped) and tools/check_game starter_game --seconds 5 pass under Xvfb on lavapipe.
  • Build with zero warnings.
  • Not run: Windows, macOS and Android builds (CI will).
<!-- claude-projects-attribution: {"github_login":"Khyretos"} --> _Requested by **Kees**_ **WIP: one big PR on purpose, to be split.** Kees (10-27): build the missing GAPS.md features locally first, split into small PRs later. This is everything built so far in one commit on top of `main` (4da51cf0). Nothing here should merge as is: the WIP prefix keeps auto-merge off. Each feature below is independent enough to cut out; the files they share are `docs/GAPS.md`, `docs/SCRIPTING.md`, `docs/README.md`, `mkdocs.yml`, `ROADMAP.md`, `engine/CMakeLists.txt`, `tests/CMakeLists.txt`, `ScriptModule.h/.cpp`, `ScriptBindings.cpp`, `ModelModule.h/.cpp` and `EngineSettings`. ## What it adds | GAPS row | Feature | Doc page | |---|---|---| | F84 | Localization: JSON string tables, CLDR plurals (~70 languages), placeholders, fallback chain, locale numbers; `kke::bidi` (Arabic shaping, right-to-left); Noto Arabic and Hebrew fonts; Lua `loc.*`; engine menus in en, nl, de, fr, es, ar, he | docs/LOCALIZATION.md | | F85 | Accessibility: subtitles (`subtitles.*`), colour-blind modes (matrix in the tone-map pass), UI scale, high contrast, reduced motion. Still 🟡: no real screen reader | docs/ACCESSIBILITY.md | | F67 | Runtime profiler: every module call timed per frame (Performance panel), captures of zones from all threads plus GPU time and memory to a Perfetto trace | docs/PROFILING.md | | F68 | Opt-in crash reports (`kke::crash`): signal or exception, backtrace, last log lines. Sending is off by default (`privacy.sendCrashReports`). POSIX only, so 🟡 | docs/CRASH_REPORTS.md | | F71 | Behaviour trees: sequence, selector, reactive, parallel, random, decorators, typed blackboard; C++ or JSON; Lua `bt.*` | docs/AI.md | | F63 | Animation compression: clips packed (about 8x smaller), smallest-three rotations, validated file format | (in GAPS row) | | F23 | BVH mocap import: `loadModel("x.bvh")` gives a skeleton and a clip for the retargeter | docs/PROCEDURAL_ANIMATION.md | | F27 | `kke::Timeline`: keyframe tracks, events, markers, spans, clips, nested timelines; Lua `timeline.*` | docs/TIMELINE.md | | F39 | Adaptive music: layered stems that follow parameters, transitions on beat/bar/segment end, stingers; Lua `music.*` | docs/AUDIO.md | | F55 | Rollback netcode (GGPO-style): input guessing, rollback and re-run, checksums, input delay | docs/NETWORKING.md | | F64, F78 | Level and world streaming: cells around foci with hysteresis and a load budget, floating origin; Lua `world.*` | docs/SCENES.md | | F81 | Weather and snow: blended types, random weather, wind, temperature, wetness, snow cover; Lua `weather.*` | docs/MOODS.md | | F25 | Blend shapes and facial animation: glTF morph targets applied before skinning (GPU and CPU paths), `kke::FaceRig` (expressions, blinking, lip-sync), Lua `face.*` | docs/FACIAL_ANIMATION.md | | F62 | Automatic LOD: up to three levels per big mesh part over its own vertices (meshoptimizer), picked by screen size with hysteresis; `graphics.autoLod`, `graphics.lodBias`, `KKE_LOD=0` | docs/LOD.md | | F38 | Only the GAPS row is corrected: HRTF already existed | | ## Limits, stated plainly - F55 rollback is a library with tests, not tried in a real networked game. - F68 crash reports: POSIX only. - F85: no screen reader. - F81 weather drives particles and wind but there is no editor tool for it. - F64/F78 do not stream the terrain's own chunks yet. - The Arabic, Hebrew, German, French, Spanish and Dutch menu strings are drafts and need a native speaker. - F25: weights animated inside a clip, VRM presets and eye look are not there. F62: no cross-fades or impostors. ## Tests - `kke_tests`: all pass (1649 in the last full run). - `python3 -W error tools/ci/check_{dependencies,game_modules,std_includes}.py` pass. - `tools/docs_site/run_recipes.py` (39 ok, `play` skipped) and `tools/check_game starter_game --seconds 5` pass under Xvfb on lavapipe. - Build with zero warnings. - Not run: Windows, macOS and Android builds (CI will).
Engine parity: 15 missing features from GAPS.md (F23/F25/F27/F39/F55/F62/F63/F64/F67/F68/F71/F78/F81/F84/F85)
Some checks failed
CI / dependencies (pull_request) Successful in 6s
CI / std-includes (pull_request) Successful in 10s
Benchmarks / benchmarks (pull_request) Successful in 4m56s
CI / shipping-build (pull_request) Successful in 13m57s
Docs site / site (pull_request) Successful in 21s
lint / megalinter (pull_request) Successful in 3m28s
CI / android (pull_request) Failing after 9m49s
CI / release-build (pull_request) Successful in 32m49s
Windows (native) / build (pull_request) Failing after 23m46s
Windows (native) / test (pull_request) Has been skipped
CI / build-test-and-coverage (pull_request) Successful in 42m8s
Windows (native) / publish (pull_request) Has been skipped
1f1c0f0f24
Localization and right-to-left text (F84), accessibility (F85: subtitles,
colour-blind modes, reduced motion, high contrast), runtime profiler with
capture files (F67), opt-in crash reports (F68, POSIX), behaviour trees (F71),
animation compression (F63), BVH mocap import (F23), timelines (F27),
adaptive music (F39), rollback netcode (F55), level and world streaming
(F64, F78), weather and snow (F81), blend shapes and facial animation (F25),
automatic mesh LOD (F62).

Each has its header and source in engine/, a doc page, Lua bindings where it
makes sense, tests, and its docs/GAPS.md row. F38 (HRTF) row corrected: it
already existed. kke_tests: 1649 pass; tools/ci/check_*.py and the cookbook
recipes pass.
Collaborator

Title

WIP: Engine parity: 15 missing features (localization, accessibility, profiler, behaviour trees, rollback, streaming, weather, blend shapes, LOD...)


User description

Requested by Kees

WIP: one big PR on purpose, to be split. Kees (10-27): build the missing GAPS.md features locally first, split into small PRs later. This is everything built so far in one commit on top of main (4da51cf0). Nothing here should merge as is: the WIP prefix keeps auto-merge off. Each feature below is independent enough to cut out; the files they share are docs/GAPS.md, docs/SCRIPTING.md, docs/README.md, mkdocs.yml, ROADMAP.md, engine/CMakeLists.txt, tests/CMakeLists.txt, ScriptModule.h/.cpp, ScriptBindings.cpp, ModelModule.h/.cpp and EngineSettings.

What it adds

GAPS row Feature Doc page
F84 Localization: JSON string tables, CLDR plurals (~70 languages), placeholders, fallback chain, locale numbers; kke::bidi (Arabic shaping, right-to-left); Noto Arabic and Hebrew fonts; Lua loc.*; engine menus in en, nl, de, fr, es, ar, he docs/LOCALIZATION.md
F85 Accessibility: subtitles (subtitles.*), colour-blind modes (matrix in the tone-map pass), UI scale, high contrast, reduced motion. Still 🟡: no real screen reader docs/ACCESSIBILITY.md
F67 Runtime profiler: every module call timed per frame (Performance panel), captures of zones from all threads plus GPU time and memory to a Perfetto trace docs/PROFILING.md
F68 Opt-in crash reports (kke::crash): signal or exception, backtrace, last log lines. Sending is off by default (privacy.sendCrashReports). POSIX only, so 🟡 docs/CRASH_REPORTS.md
F71 Behaviour trees: sequence, selector, reactive, parallel, random, decorators, typed blackboard; C++ or JSON; Lua bt.* docs/AI.md
F63 Animation compression: clips packed (about 8x smaller), smallest-three rotations, validated file format (in GAPS row)
F23 BVH mocap import: loadModel("x.bvh") gives a skeleton and a clip for the retargeter docs/PROCEDURAL_ANIMATION.md
F27 kke::Timeline: keyframe tracks, events, markers, spans, clips, nested timelines; Lua timeline.* docs/TIMELINE.md
F39 Adaptive music: layered stems that follow parameters, transitions on beat/bar/segment end, stingers; Lua music.* docs/AUDIO.md
F55 Rollback netcode (GGPO-style): input guessing, rollback and re-run, checksums, input delay docs/NETWORKING.md
F64, F78 Level and world streaming: cells around foci with hysteresis and a load budget, floating origin; Lua world.* docs/SCENES.md
F81 Weather and snow: blended types, random weather, wind, temperature, wetness, snow cover; Lua weather.* docs/MOODS.md
F25 Blend shapes and facial animation: glTF morph targets applied before skinning (GPU and CPU paths), kke::FaceRig (expressions, blinking, lip-sync), Lua face.* docs/FACIAL_ANIMATION.md
F62 Automatic LOD: up to three levels per big mesh part over its own vertices (meshoptimizer), picked by screen size with hysteresis; graphics.autoLod, graphics.lodBias, KKE_LOD=0 docs/LOD.md
F38 Only the GAPS row is corrected: HRTF already existed

Limits, stated plainly

  • F55 rollback is a library with tests, not tried in a real networked game.
  • F68 crash reports: POSIX only.
  • F85: no screen reader.
  • F81 weather drives particles and wind but there is no editor tool for it.
  • F64/F78 do not stream the terrain's own chunks yet.
  • The Arabic, Hebrew, German, French, Spanish and Dutch menu strings are drafts and need a native speaker.
  • F25: weights animated inside a clip, VRM presets and eye look are not there. F62: no cross-fades or impostors.

Tests

  • kke_tests: all pass (1649 in the last full run).
  • python3 -W error tools/ci/check_{dependencies,game_modules,std_includes}.py pass.
  • tools/docs_site/run_recipes.py (39 ok, play skipped) and tools/check_game starter_game --seconds 5 pass under Xvfb on lavapipe.
  • Build with zero warnings.
  • Not run: Windows, macOS and Android builds (CI will).

PR Type

Enhancement, Tests


Description

  • Adds localization, accessibility, and profiler systems

  • Implements behaviour trees, timelines, and adaptive music

  • Introduces world streaming, weather, and rollback netcode

  • Includes LOD generation, mocap import, and crash reporting


Diagram Walkthrough

flowchart LR
  A["Core Systems"] --> B["Localization"]
  A --> C["Accessibility"]
  A --> D["Profiler"]
  E["AI & Animation"] --> F["Behaviour Trees"]
  E --> G["Timelines"]
  E --> H["Mocap & LOD"]
  I["World & Netcode"] --> J["Streaming"]
  I --> K["Rollback"]
  I --> L["Weather"]
  M["Audio & UI"] --> N["Music Director"]
  M --> O["Subtitles"]
  P["Reliability"] --> Q["Crash Reporter"]

File Walkthrough

Relevant files
Enhancement
21 files
BehaviorTree.cpp
Implements full behaviour tree engine with nodes, blackboard and JSON
parsing
+1057/-0
Timeline.cpp
Implements timeline system with tracks, events, clips and nested
timelines
+714/-0 
Localization.cpp
Adds localization with JSON tables, CLDR plurals and RTL support
+709/-0 
ScriptBindings.cpp
Binds LOD, facial animation and adaptive music to Lua       
+88/-0   
Application.h
Adds motion blur request flag for accessibility settings 
+1/-0     
MusicDirector.cpp
Adaptive music director with segments and stingers             
+511/-0 
Bidi.cpp
Right-to-left text shaping and bidi algorithm                       
+431/-0 
Mesh.h
Added vertex buffer accessor for LOD support                         
+3/-0     
ScriptBehavior.cpp
Lua bindings for behaviour trees with blackboard access   
+530/-0 
Mocap.cpp
BVH mocap import and motion cleaning utilities                     
+476/-0 
ModelModule.cpp
Automatic LOD, blend shapes and facial animation support 
+231/-23
AnimCompress.cpp
Animation compression with quantization and key reduction
+444/-0 
CrashReporter.cpp
POSIX crash reporting with backtraces and opt-in upload   
+309/-0 
Weather.cpp
Implements weather system with presets, transitions, and updates
+395/-0 
Rollback.cpp
Adds rollback netcode for deterministic simulation and desync
detection
+491/-0 
ScriptTimeline.cpp
Adds Lua bindings for timeline creation, playback, and event delivery
+433/-0 
Renderer.cpp
Extends post-pass push constants to support color-blind correction
rows
+13/-2   
Profiler.cpp
Implementation of runtime profiler with capture support   
+429/-0 
ScriptWorld.cpp
Streaming world implementation with cell loading                 
+323/-0 
FaceRig.cpp
Face rig system with blend shapes and lip sync                     
+258/-0 
ScriptWeather.cpp
Weather system with particles and lighting effects             
+297/-0 
Tests
14 files
test_music.cpp
Tests adaptive music director with scheduled voices and transitions
+811/-0 
test_script_weather.cpp
Tests Lua weather system including rain, snow, lighting and hooks
+363/-0 
test_timeline.cpp
Adds comprehensive unit tests for timeline evaluation and playback
+628/-0 
test_behavior_tree.cpp
Comprehensive behavior tree unit tests covering all node types and
factory
+544/-0 
test_rollback.cpp
Rollback netcode tests for prediction, lag compensation, and desync
detection
+621/-0 
test_world_stream.cpp
World streaming tests for cell loading, unloading, and origin shifting
+499/-0 
test_script_world.cpp
Lua scripting tests for world streaming hooks and cell management
functions
+322/-0 
test_mocap.cpp
BVH mocap import and motion cleaning tests                             
+432/-0 
test_weather.cpp
Weather system simulation and event tests                               
+529/-0 
test_anim_compress.cpp
Tests animation compression quality, quantization, and file
round-trips
+414/-0 
test_localization.cpp
Tests localization loading, plurals, RTL, numbers, and fallback chains
+388/-0 
test_script_timeline.cpp
Tests for ScriptTimeline Lua bindings and features             
+315/-0 
test_script_behavior.cpp
Tests for behaviour tree Lua scripting system                       
+275/-0 
test_lod_chain.cpp
Tests for automatic mesh LOD generation algorithm               
+135/-0 
Bug fix
1 files
CameraDirector.cpp
Reduced motion check for camera shake accessibility           
+2/-0     
Additional files
92 files
ROADMAP.md +6/-0     
NOTO-LICENSE.txt +7/-0     
ar.json +65/-0   
de.json +62/-0   
en.json +62/-0   
es.json +62/-0   
fr.json +62/-0   
he.json +65/-0   
nl.json +62/-0   
ACCESSIBILITY.md +61/-0   
AI.md +63/-5   
AUDIO.md +73/-0   
CRASH_REPORTS.md +79/-0   
DEPENDENCIES.md +2/-0     
FACIAL_ANIMATION.md +71/-0   
GAPS.md +16/-16 
LOCALIZATION.md +137/-0 
LOD.md +48/-0   
MOODS.md +62/-0   
MOVEMENT.md +43/-0   
NETWORKING.md +75/-1   
PROCEDURAL_ANIMATION.md +40/-0   
PROFILING.md +80/-0   
README.md +4/-0     
SCENES.md +59/-0   
SCRIPTING.md +24/-0   
TIMELINE.md +132/-0 
CMakeLists.txt +28/-1   
Accessibility.h +86/-0   
AnimCompress.h +113/-0 
Animator.h +16/-1   
AudioMixer.h +30/-0   
Bidi.h +52/-0   
CrashReporter.h +59/-0   
EngineSettings.h +27/-0   
FaceRig.h +103/-0 
Localization.h +134/-0 
LodChain.h +48/-0   
Log.h +8/-0     
Mocap.h +97/-0   
ModelAsset.h +16/-0   
Morph.h +34/-0   
MusicDirector.h +209/-0 
Profiler.h +216/-0 
Renderer.h +3/-0     
Rollback.h +209/-0 
ScriptBehavior.h +80/-0   
ScriptMusic.h +36/-0   
ScriptTimeline.h +53/-0   
ScriptWeather.h +73/-0   
ScriptWorld.h +42/-0   
Subtitles.h +62/-0   
Timeline.h +220/-0 
Weather.h +162/-0 
WorldStream.h +185/-0 
BehaviorTree.h +273/-0 
GameShellModule.h +3/-0     
ModelModule.h +75/-2   
ScriptModule.h +17/-0   
UiModule.h +17/-0   
Accessibility.cpp +184/-0 
Animator.cpp +32/-2   
Application.cpp +114/-4 
AudioMixer.cpp +111/-1 
EngineSettings.cpp +39/-2   
GltfAsset.cpp +37/-1   
LodChain.cpp +50/-0   
Log.cpp +70/-1   
ModelAsset.cpp +3/-0     
Morph.cpp +71/-0   
ScriptMusic.cpp +177/-0 
Subtitles.cpp +86/-0   
WorldStream.cpp +293/-0 
GameShellModule.cpp +125/-2 
ScriptLocale.cpp +221/-0 
ScriptModule.cpp +34/-0   
ScriptProfile.cpp +108/-0 
SettingsModule.cpp +5/-0     
StatsModule.cpp +36/-0   
UiModule.cpp +108/-0 
mkdocs.yml +7/-0     
tonemap_post.frag +6/-0     
CMakeLists.txt +21/-0   
test_accessibility.cpp +206/-0 
test_bidi.cpp +108/-0 
test_crash_reporter.cpp +196/-0 
test_engine_settings.cpp +68/-0   
test_face_rig.cpp +181/-0 
test_morph.cpp +171/-0 
test_profiler.cpp +306/-0 
test_script_music.cpp +262/-0 
morph_test_model.py +105/-0 

## Title WIP: Engine parity: 15 missing features (localization, accessibility, profiler, behaviour trees, rollback, streaming, weather, blend shapes, LOD...) ___ ### **User description** <!-- claude-projects-attribution: {"github_login":"Khyretos"} --> _Requested by **Kees**_ **WIP: one big PR on purpose, to be split.** Kees (10-27): build the missing GAPS.md features locally first, split into small PRs later. This is everything built so far in one commit on top of `main` (4da51cf0). Nothing here should merge as is: the WIP prefix keeps auto-merge off. Each feature below is independent enough to cut out; the files they share are `docs/GAPS.md`, `docs/SCRIPTING.md`, `docs/README.md`, `mkdocs.yml`, `ROADMAP.md`, `engine/CMakeLists.txt`, `tests/CMakeLists.txt`, `ScriptModule.h/.cpp`, `ScriptBindings.cpp`, `ModelModule.h/.cpp` and `EngineSettings`. ## What it adds | GAPS row | Feature | Doc page | |---|---|---| | F84 | Localization: JSON string tables, CLDR plurals (~70 languages), placeholders, fallback chain, locale numbers; `kke::bidi` (Arabic shaping, right-to-left); Noto Arabic and Hebrew fonts; Lua `loc.*`; engine menus in en, nl, de, fr, es, ar, he | docs/LOCALIZATION.md | | F85 | Accessibility: subtitles (`subtitles.*`), colour-blind modes (matrix in the tone-map pass), UI scale, high contrast, reduced motion. Still 🟡: no real screen reader | docs/ACCESSIBILITY.md | | F67 | Runtime profiler: every module call timed per frame (Performance panel), captures of zones from all threads plus GPU time and memory to a Perfetto trace | docs/PROFILING.md | | F68 | Opt-in crash reports (`kke::crash`): signal or exception, backtrace, last log lines. Sending is off by default (`privacy.sendCrashReports`). POSIX only, so 🟡 | docs/CRASH_REPORTS.md | | F71 | Behaviour trees: sequence, selector, reactive, parallel, random, decorators, typed blackboard; C++ or JSON; Lua `bt.*` | docs/AI.md | | F63 | Animation compression: clips packed (about 8x smaller), smallest-three rotations, validated file format | (in GAPS row) | | F23 | BVH mocap import: `loadModel("x.bvh")` gives a skeleton and a clip for the retargeter | docs/PROCEDURAL_ANIMATION.md | | F27 | `kke::Timeline`: keyframe tracks, events, markers, spans, clips, nested timelines; Lua `timeline.*` | docs/TIMELINE.md | | F39 | Adaptive music: layered stems that follow parameters, transitions on beat/bar/segment end, stingers; Lua `music.*` | docs/AUDIO.md | | F55 | Rollback netcode (GGPO-style): input guessing, rollback and re-run, checksums, input delay | docs/NETWORKING.md | | F64, F78 | Level and world streaming: cells around foci with hysteresis and a load budget, floating origin; Lua `world.*` | docs/SCENES.md | | F81 | Weather and snow: blended types, random weather, wind, temperature, wetness, snow cover; Lua `weather.*` | docs/MOODS.md | | F25 | Blend shapes and facial animation: glTF morph targets applied before skinning (GPU and CPU paths), `kke::FaceRig` (expressions, blinking, lip-sync), Lua `face.*` | docs/FACIAL_ANIMATION.md | | F62 | Automatic LOD: up to three levels per big mesh part over its own vertices (meshoptimizer), picked by screen size with hysteresis; `graphics.autoLod`, `graphics.lodBias`, `KKE_LOD=0` | docs/LOD.md | | F38 | Only the GAPS row is corrected: HRTF already existed | | ## Limits, stated plainly - F55 rollback is a library with tests, not tried in a real networked game. - F68 crash reports: POSIX only. - F85: no screen reader. - F81 weather drives particles and wind but there is no editor tool for it. - F64/F78 do not stream the terrain's own chunks yet. - The Arabic, Hebrew, German, French, Spanish and Dutch menu strings are drafts and need a native speaker. - F25: weights animated inside a clip, VRM presets and eye look are not there. F62: no cross-fades or impostors. ## Tests - `kke_tests`: all pass (1649 in the last full run). - `python3 -W error tools/ci/check_{dependencies,game_modules,std_includes}.py` pass. - `tools/docs_site/run_recipes.py` (39 ok, `play` skipped) and `tools/check_game starter_game --seconds 5` pass under Xvfb on lavapipe. - Build with zero warnings. - Not run: Windows, macOS and Android builds (CI will). ___ ### **PR Type** Enhancement, Tests ___ ### **Description** - Adds localization, accessibility, and profiler systems - Implements behaviour trees, timelines, and adaptive music - Introduces world streaming, weather, and rollback netcode - Includes LOD generation, mocap import, and crash reporting ___ ### Diagram Walkthrough ```mermaid flowchart LR A["Core Systems"] --> B["Localization"] A --> C["Accessibility"] A --> D["Profiler"] E["AI & Animation"] --> F["Behaviour Trees"] E --> G["Timelines"] E --> H["Mocap & LOD"] I["World & Netcode"] --> J["Streaming"] I --> K["Rollback"] I --> L["Weather"] M["Audio & UI"] --> N["Music Director"] M --> O["Subtitles"] P["Reliability"] --> Q["Crash Reporter"] ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>21 files</summary><table> <tr> <td><strong>BehaviorTree.cpp</strong><dd><code>Implements full behaviour tree engine with nodes, blackboard and JSON </code><br><code>parsing</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ai/BehaviorTree.cpp">+1057/-0</a></td> </tr> <tr> <td><strong>Timeline.cpp</strong><dd><code>Implements timeline system with tracks, events, clips and nested </code><br><code>timelines</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Timeline.cpp">+714/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Localization.cpp</strong><dd><code>Adds localization with JSON tables, CLDR plurals and RTL support</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Localization.cpp">+709/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptBindings.cpp</strong><dd><code>Binds LOD, facial animation and adaptive music to Lua</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/ScriptBindings.cpp">+88/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Application.h</strong><dd><code>Adds motion blur request flag for accessibility settings</code>&nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Application.h">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>MusicDirector.cpp</strong><dd><code>Adaptive music director with segments and stingers</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/MusicDirector.cpp">+511/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Bidi.cpp</strong><dd><code>Right-to-left text shaping and bidi algorithm</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Bidi.cpp">+431/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Mesh.h</strong><dd><code>Added vertex buffer accessor for LOD support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Mesh.h">+3/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptBehavior.cpp</strong><dd><code>Lua bindings for behaviour trees with blackboard access</code>&nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ScriptBehavior.cpp">+530/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Mocap.cpp</strong><dd><code>BVH mocap import and motion cleaning utilities</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Mocap.cpp">+476/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ModelModule.cpp</strong><dd><code>Automatic LOD, blend shapes and facial animation support</code>&nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/ModelModule.cpp">+231/-23</a></td> </tr> <tr> <td><strong>AnimCompress.cpp</strong><dd><code>Animation compression with quantization and key reduction</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/AnimCompress.cpp">+444/-0</a>&nbsp; </td> </tr> <tr> <td><strong>CrashReporter.cpp</strong><dd><code>POSIX crash reporting with backtraces and opt-in upload</code>&nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/CrashReporter.cpp">+309/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Weather.cpp</strong><dd><code>Implements weather system with presets, transitions, and updates</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Weather.cpp">+395/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Rollback.cpp</strong><dd><code>Adds rollback netcode for deterministic simulation and desync </code><br><code>detection</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Rollback.cpp">+491/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptTimeline.cpp</strong><dd><code>Adds Lua bindings for timeline creation, playback, and event delivery</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ScriptTimeline.cpp">+433/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Renderer.cpp</strong><dd><code>Extends post-pass push constants to support color-blind correction </code><br><code>rows</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Renderer.cpp">+13/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Profiler.cpp</strong><dd><code>Implementation of runtime profiler with capture support</code>&nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Profiler.cpp">+429/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptWorld.cpp</strong><dd><code>Streaming world implementation with cell loading</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ScriptWorld.cpp">+323/-0</a>&nbsp; </td> </tr> <tr> <td><strong>FaceRig.cpp</strong><dd><code>Face rig system with blend shapes and lip sync</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/FaceRig.cpp">+258/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptWeather.cpp</strong><dd><code>Weather system with particles and lighting effects</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ScriptWeather.cpp">+297/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>14 files</summary><table> <tr> <td><strong>test_music.cpp</strong><dd><code>Tests adaptive music director with scheduled voices and transitions</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_music.cpp">+811/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_script_weather.cpp</strong><dd><code>Tests Lua weather system including rain, snow, lighting and hooks</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_script_weather.cpp">+363/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_timeline.cpp</strong><dd><code>Adds comprehensive unit tests for timeline evaluation and playback</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_timeline.cpp">+628/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_behavior_tree.cpp</strong><dd><code>Comprehensive behavior tree unit tests covering all node types and </code><br><code>factory</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_behavior_tree.cpp">+544/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_rollback.cpp</strong><dd><code>Rollback netcode tests for prediction, lag compensation, and desync </code><br><code>detection</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_rollback.cpp">+621/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_world_stream.cpp</strong><dd><code>World streaming tests for cell loading, unloading, and origin shifting</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_world_stream.cpp">+499/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_script_world.cpp</strong><dd><code>Lua scripting tests for world streaming hooks and cell management </code><br><code>functions</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_script_world.cpp">+322/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_mocap.cpp</strong><dd><code>BVH mocap import and motion cleaning tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_mocap.cpp">+432/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_weather.cpp</strong><dd><code>Weather system simulation and event tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_weather.cpp">+529/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_anim_compress.cpp</strong><dd><code>Tests animation compression quality, quantization, and file </code><br><code>round-trips</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_anim_compress.cpp">+414/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_localization.cpp</strong><dd><code>Tests localization loading, plurals, RTL, numbers, and fallback chains</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_localization.cpp">+388/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_script_timeline.cpp</strong><dd><code>Tests for ScriptTimeline Lua bindings and features</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_script_timeline.cpp">+315/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_script_behavior.cpp</strong><dd><code>Tests for behaviour tree Lua scripting system</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_script_behavior.cpp">+275/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_lod_chain.cpp</strong><dd><code>Tests for automatic mesh LOD generation algorithm</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_lod_chain.cpp">+135/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Bug fix</strong></td><td><details><summary>1 files</summary><table> <tr> <td><strong>CameraDirector.cpp</strong><dd><code>Reduced motion check for camera shake accessibility</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/CameraDirector.cpp">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>92 files</summary><table> <tr> <td><strong>ROADMAP.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/ROADMAP.md">+6/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>NOTO-LICENSE.txt</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/fonts/NOTO-LICENSE.txt">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>ar.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/ar.json">+65/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>de.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/de.json">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>en.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/en.json">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>es.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/es.json">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>fr.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/fr.json">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>he.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/he.json">+65/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>nl.json</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/assets/locales/nl.json">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ACCESSIBILITY.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/ACCESSIBILITY.md">+61/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>AI.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/AI.md">+63/-5</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>AUDIO.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/AUDIO.md">+73/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>CRASH_REPORTS.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/CRASH_REPORTS.md">+79/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>DEPENDENCIES.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/DEPENDENCIES.md">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>FACIAL_ANIMATION.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/FACIAL_ANIMATION.md">+71/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>GAPS.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/GAPS.md">+16/-16</a>&nbsp; </td> </tr> <tr> <td><strong>LOCALIZATION.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/LOCALIZATION.md">+137/-0</a>&nbsp; </td> </tr> <tr> <td><strong>LOD.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/LOD.md">+48/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>MOODS.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/MOODS.md">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>MOVEMENT.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/MOVEMENT.md">+43/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>NETWORKING.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/NETWORKING.md">+75/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>PROCEDURAL_ANIMATION.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/PROCEDURAL_ANIMATION.md">+40/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>PROFILING.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/PROFILING.md">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/README.md">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>SCENES.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/SCENES.md">+59/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>SCRIPTING.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/SCRIPTING.md">+24/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>TIMELINE.md</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/docs/TIMELINE.md">+132/-0</a>&nbsp; </td> </tr> <tr> <td><strong>CMakeLists.txt</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/CMakeLists.txt">+28/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Accessibility.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Accessibility.h">+86/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>AnimCompress.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/AnimCompress.h">+113/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Animator.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Animator.h">+16/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>AudioMixer.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/AudioMixer.h">+30/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Bidi.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Bidi.h">+52/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>CrashReporter.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/CrashReporter.h">+59/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>EngineSettings.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/EngineSettings.h">+27/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>FaceRig.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/FaceRig.h">+103/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Localization.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Localization.h">+134/-0</a>&nbsp; </td> </tr> <tr> <td><strong>LodChain.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/LodChain.h">+48/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Log.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Log.h">+8/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Mocap.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Mocap.h">+97/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ModelAsset.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ModelAsset.h">+16/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Morph.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Morph.h">+34/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>MusicDirector.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/MusicDirector.h">+209/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Profiler.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Profiler.h">+216/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Renderer.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Renderer.h">+3/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Rollback.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Rollback.h">+209/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptBehavior.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ScriptBehavior.h">+80/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptMusic.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ScriptMusic.h">+36/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptTimeline.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ScriptTimeline.h">+53/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptWeather.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ScriptWeather.h">+73/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptWorld.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ScriptWorld.h">+42/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Subtitles.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Subtitles.h">+62/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Timeline.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Timeline.h">+220/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Weather.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/Weather.h">+162/-0</a>&nbsp; </td> </tr> <tr> <td><strong>WorldStream.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/WorldStream.h">+185/-0</a>&nbsp; </td> </tr> <tr> <td><strong>BehaviorTree.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/ai/BehaviorTree.h">+273/-0</a>&nbsp; </td> </tr> <tr> <td><strong>GameShellModule.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/modules/GameShellModule.h">+3/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>ModelModule.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/modules/ModelModule.h">+75/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptModule.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/modules/ScriptModule.h">+17/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>UiModule.h</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/include/kke/modules/UiModule.h">+17/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Accessibility.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Accessibility.cpp">+184/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Animator.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Animator.cpp">+32/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Application.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Application.cpp">+114/-4</a>&nbsp; </td> </tr> <tr> <td><strong>AudioMixer.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/AudioMixer.cpp">+111/-1</a>&nbsp; </td> </tr> <tr> <td><strong>EngineSettings.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/EngineSettings.cpp">+39/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>GltfAsset.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/GltfAsset.cpp">+37/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>LodChain.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/LodChain.cpp">+50/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Log.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Log.cpp">+70/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ModelAsset.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ModelAsset.cpp">+3/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Morph.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Morph.cpp">+71/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptMusic.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ScriptMusic.cpp">+177/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Subtitles.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Subtitles.cpp">+86/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>WorldStream.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/WorldStream.cpp">+293/-0</a>&nbsp; </td> </tr> <tr> <td><strong>GameShellModule.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/GameShellModule.cpp">+125/-2</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptLocale.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/ScriptLocale.cpp">+221/-0</a>&nbsp; </td> </tr> <tr> <td><strong>ScriptModule.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/ScriptModule.cpp">+34/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ScriptProfile.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/ScriptProfile.cpp">+108/-0</a>&nbsp; </td> </tr> <tr> <td><strong>SettingsModule.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/SettingsModule.cpp">+5/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>StatsModule.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/StatsModule.cpp">+36/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>UiModule.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/modules/UiModule.cpp">+108/-0</a>&nbsp; </td> </tr> <tr> <td><strong>mkdocs.yml</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/mkdocs.yml">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>tonemap_post.frag</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/shaders/tonemap_post.frag">+6/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>CMakeLists.txt</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/CMakeLists.txt">+21/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_accessibility.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_accessibility.cpp">+206/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_bidi.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_bidi.cpp">+108/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_crash_reporter.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_crash_reporter.cpp">+196/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_engine_settings.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_engine_settings.cpp">+68/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_face_rig.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_face_rig.cpp">+181/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_morph.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_morph.cpp">+171/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_profiler.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_profiler.cpp">+306/-0</a>&nbsp; </td> </tr> <tr> <td><strong>test_script_music.cpp</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_script_music.cpp">+262/-0</a>&nbsp; </td> </tr> <tr> <td><strong>morph_test_model.py</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tools/morph_test_model.py">+105/-0</a>&nbsp; </td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
Collaborator

PR Reviewer Guide 🔍

📋 Focus areas: 🔴 12 open · ✅ 0 handled. Every push re-checks them; a handled area moves to Resolved findings at the bottom.

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Potential Integer Overflow in Parallel Node

In the Parallel node's onTick method, the code sums up success and failure counts from child nodes into ok and bad variables. If a behavior tree is constructed with a very large number of children (exceeding the maximum value of size_t), this summation could overflow, leading to incorrect status determination (e.g., reporting success when it should fail). While unlikely in typical use cases, robustness suggests using a type that can safely hold the sum or checking for potential overflow if the number of children is expected to be large.

size_t ok = 0, bad = 0;
for (size_t i = 0; i < n; ++i) {
    if (m_done[i] == 0) {
        const BtStatus s = m_children[i]->tick(c);
        if (s == BtStatus::Success) m_done[i] = 1;
        else if (s == BtStatus::Failure) m_done[i] = 2;
    }
    ok += m_done[i] == 1;
    bad += m_done[i] == 2;
}

Hard-coded Audio Sample Rate

The test file defines a constant kRate = 48000 for the audio sample rate. This hard-codes the test environment to a specific sample rate, which may not match the actual runtime configuration of the engine or other tests. If the engine supports configurable sample rates, these tests might fail or produce incorrect timing results on systems configured differently. It would be better to either use a default value from the engine's configuration or explicitly document why 48kHz is chosen as the test standard.

constexpr int kRate = 48000;

Missing Error Handling for File I/O

The loadFile function reads a file into a string stream and then passes it to loadJson. If the file is empty or contains only whitespace, nlohmann::json::parse will throw an exception. While the exception is caught, the error message returned to the caller will be "not JSON: [exception message]" instead of a more specific "file is empty" or similar. This makes debugging localization file issues harder. Additionally, if the file path is invalid, the error message is just "can't read ", which is fine, but the combination of these cases could lead to confusing logs.

bool Localization::loadFile(const std::string& path, std::string* error) {
    std::ifstream in(path, std::ios::binary);
    if (!in) {
        if (error) *error = "can't read " + path;
        return false;
    }
    std::stringstream text;
    text << in.rdbuf();
    std::string inner;
    if (!loadJson(text.str(), &inner)) {
        if (error) *error = path + ": " + inner;
        return false;
    }
    return true;
}

Hardcoded Test Data in Source

The test ABossFightWithPhases (line 416) uses a large JSON string literal directly in the source code. While this is common for small configs, it makes the test harder to read and maintain compared to loading from a separate file or using a helper function. It also increases the risk of typos in the JSON syntax within the C++ source.

const json spec = json::parse(R"({
    "type": "reactive_selector", "label": "boss",
    "children": [
        { "type": "reactive_sequence", "label": "phase 3", "children": [
            { "type": "check", "key": "hp", "op": "<", "value": 30 },
            { "type": "action", "name": "rage" } ] },
        { "type": "reactive_sequence", "label": "phase 2", "children": [
            { "type": "check", "key": "hp", "op": "<", "value": 60 },
            { "type": "cooldown", "seconds": 5, "child": { "type": "action", "name": "summon" } },
            "slam" ] },
        { "type": "reactive_sequence", "label": "phase 1", "children": [ "playerNear", "charge" ] }
    ] })");

Potential Integer Overflow in Random Sequence

In the RandomSequenceRunsEveryChildOnce test (line 244), a BehaviorTree is created with a seed of 3. The btRandomSequence constructor likely uses this seed to initialize a random number generator. If the implementation of btRandomSequence does not properly handle the seed or if the internal state machine relies on integer arithmetic that can overflow given the number of children (4 in this case), it might produce incorrect ordering or fail to visit all children. While unlikely with modern RNGs, it's worth noting that relying on a hardcoded small seed for a test that verifies randomness properties is slightly risky; a larger variety of seeds or a check against multiple runs would be more robust.

TEST(BehaviorTree, RandomSequenceRunsEveryChildOnce) {
    auto count = std::make_shared<int>(0);
    BtChildren c;
    for (int i = 0; i < 4; ++i)
        c.push_back(btAction([count](BtContext&) {
            ++*count;
            return S;
        }));
    BehaviorTree t(btRandomSequence(std::move(c)), 3);
    EXPECT_EQ(t.tick(0.1f), S);
    EXPECT_EQ(*count, 4);
}

Missing Unicode Normalization

The decode function in Bidi.cpp handles UTF-8 decoding but does not perform Unicode normalization. This can lead to inconsistent text representation when combining characters (like Arabic or Hebrew) are involved, potentially causing display issues or incorrect bidirectional algorithm behavior. For robust localization support, normalization should be applied before processing.

std::u32string decode(std::string_view s) {
    std::u32string out;
    out.reserve(s.size());
    for (size_t i = 0; i < s.size();) {
        const unsigned char b = static_cast<unsigned char>(s[i]);
        int extra = 0;
        char32_t c = 0;
        if (b < 0x80) c = b;
        else if ((b & 0xE0) == 0xC0) { c = b & 0x1F; extra = 1; }
        else if ((b & 0xF0) == 0xE0) { c = b & 0x0F; extra = 2; }
        else if ((b & 0xF8) == 0xF0) { c = b & 0x07; extra = 3; }
        else { out.push_back(0xFFFD); ++i; continue; }
        if (i + static_cast<size_t>(extra) >= s.size() && extra > 0) { // cut off
            out.push_back(0xFFFD);
            ++i;
            continue;
        }
        bool ok = true;
        for (int k = 1; k <= extra; ++k) {
            const unsigned char cont = static_cast<unsigned char>(s[i + static_cast<size_t>(k)]);
            if ((cont & 0xC0) != 0x80) { ok = false; break; }
            c = (c << 6) | (cont & 0x3F);
        }
        if (!ok) {
            out.push_back(0xFFFD);
            ++i;
            continue;
        }
        out.push_back(c);
        i += static_cast<size_t>(extra) + 1;
    }
    return out;
}

Incomplete Arabic Ligature Support

The lamAlef function only handles specific alef characters (0x622, 0x623, 0x625, 0x627) for lam-alef ligatures. However, Arabic script includes other forms of alef that may require ligature handling depending on context. This incomplete coverage could result in incorrect shaping for certain Arabic words containing lam followed by different alef variants.

char32_t lamAlef(char32_t alef, bool final) {
    switch (alef) {
    case 0x622: return final ? 0xFEF6 : 0xFEF5;
    case 0x623: return final ? 0xFEF8 : 0xFEF7;
    case 0x625: return final ? 0xFEFA : 0xFEF9;
    case 0x627: return final ? 0xFEFC : 0xFEFB;
    default: return 0;
    }
}

Potential Resource Leak in Morph Parts Cleanup
In the remove method, morph parts are retired using std::make_shared<std::vector<MorphPart>>(std::move(inst.morphParts)). While this moves the data, if inst.morphParts contains shared pointers to GPU buffers or other resources that need explicit cleanup beyond just moving the vector, there might be a leak. Ensure all underlying resources held by MorphPart are properly released when the instance is destroyed.

Potential Integer Overflow

In Weather::update, the wind speed calculation multiplies s.wind (up to 100) by (1.0f + s.gust * n). Since n can reach approximately 1.0, the result can be around 200 m/s. While this is within float range, if s.wind or s.gust were to increase further in future modifications, or if the logic changes, this could lead to unrealistic physics or potential overflow in downstream calculations that expect reasonable wind speeds. The current bounds check for wind (0 to 100) is applied at definition time but not enforced during runtime updates where gusts are applied.

s.windSpeed = std::max(0.0f, s.wind * (1.0f + s.gust * n));

Missing Validation for Random Weather Parameters

The randomWeather function accepts minSeconds and maxSeconds parameters but does not validate them against the hardcoded defaults or ensure they are positive. If a user passes negative values or zero, the logic std::max(1.0f, std::isfinite(minSeconds) ? minSeconds : 120.0f) might behave unexpectedly if minSeconds is negative (it would clamp to 1.0, which is fine, but the intent might be violated). More critically, if minSeconds is NaN or infinite, the behavior is undefined due to std::isfinite checks only handling finite numbers, potentially leading to infinite loops or incorrect timing if m_hold becomes invalid.

    m_randomMin = std::max(1.0f, std::isfinite(minSeconds) ? minSeconds : 120.0f);
    m_randomMax = std::max(m_randomMin, std::isfinite(maxSeconds) ? maxSeconds : 300.0f);
    m_randomTransition = std::max(0.0f, std::isfinite(transition) ? transition : 40.0f);
    m_hold = static_cast<double>(m_randomMin + unit() * (m_randomMax - m_randomMin));
}

Potential Out-of-Bounds Access in cleanMotion

In cleanMotion, the loop for (size_t i = 1; i + 1 < f.size(); ++i) accesses f[i - 1], f[i], and f[i + 1]. This is safe as long as f.size() >= 3. However, the condition if ((o.spikeDegrees > 0.0f || o.spikeMetres > 0.0f) && f.size() > 2) ensures f.size() is at least 3 before entering the loop. But inside the smoothing section, there is a similar check if (o.smoothSeconds > 0.0f && f.size() > 2). If f.size() is exactly 2, the loop for (int i = 0; i < n; ++i) where n = static_cast<int>(f.size()) will run for i=0 and i=1. Inside, it accesses src[static_cast<size_t>(j)][b] where j ranges from -radius to radius. If radius is greater than 0 (which it is if sigma > 0), then j can be negative or exceed n-1, causing out-of-bounds access on src.

for (int k = -radius; k <= radius; ++k) {
    const int j = i + k;
    if (j < 0 || j >= n) continue;
    const float w = weight[static_cast<size_t>(std::abs(k))];
    const BoneTRS& x = src[static_cast<size_t>(j)][b];
    t += w * x.t;
    s += w * x.s;
    const glm::quat r = glm::dot(ref, x.r) < 0.0f ? -x.r : x.r;
    q += w * glm::vec4(r.x, r.y, r.z, r.w);
    total += w;

Potential Division by Zero in reduceKeys

In reduceKeys, the line const float t = static_cast<float>(i - a) / static_cast<float>(b - a); calculates a normalized time. If a and b are equal (which shouldn't happen given the stack logic if (at - a > 1) stack.emplace_back(a, at); if (b - at > 1) stack.emplace_back(at, b);), it would cause division by zero. However, the initial stack push stack.emplace_back(0, n - 1) ensures b - a is n - 1. If n == 1, the function returns early. If n == 2, the loop for (size_t i = a + 1; i < b; ++i) doesn't execute, so worst remains 0.0f, and no new keys are added. The division only happens if i iterates, implying b - a > 1. Thus, this is likely safe, but the comment "Ramer-Douglas-Peucker over time" suggests a standard algorithm where a and b are distinct indices. The code seems robust here, but it's worth noting that if b - a were somehow 0 due to a bug in stack management, it would crash.

const float t = static_cast<float>(i - a) / static_cast<float>(b - a);
const float e = err(v[i], mix(v[a], v[b], t));


ℹ️ Chunked review: the diff exceeded the model token budget, so it was reviewed in 8 chunks and the per-chunk results were merged.


⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • engine/src/FaceRig.cpp
  • engine/src/ScriptWeather.cpp
  • tests/test_script_behavior.cpp
  • tests/test_script_music.cpp
  • tests/test_profiler.cpp
  • engine/src/WorldStream.cpp
  • engine/src/CrashReporter.cpp
  • tests/test_morph.cpp
  • engine/src/Application.cpp
  • engine/src/modules/ScriptLocale.cpp
  • tests/test_accessibility.cpp
  • tests/test_face_rig.cpp
  • engine/src/modules/GameShellModule.cpp
  • engine/src/modules/UiModule.cpp
  • tests/test_lod_chain.cpp
  • engine/src/EngineSettings.cpp
  • tests/test_crash_reporter.cpp
  • engine/src/Accessibility.cpp
  • engine/src/ScriptMusic.cpp
  • engine/src/AudioMixer.cpp
  • engine/src/modules/ScriptBindings.cpp
  • tests/test_bidi.cpp
  • engine/src/modules/ScriptProfile.cpp
  • engine/src/Log.cpp
  • engine/src/Animator.cpp
  • tests/test_engine_settings.cpp
  • engine/src/GltfAsset.cpp
  • engine/src/Morph.cpp
  • engine/src/modules/ScriptModule.cpp
  • engine/src/Subtitles.cpp
  • engine/src/Renderer.cpp
  • engine/src/LodChain.cpp
  • engine/src/modules/StatsModule.cpp
  • engine/src/modules/SettingsModule.cpp
  • engine/src/ModelAsset.cpp
  • engine/src/CameraDirector.cpp
  • shaders/tonemap_post.frag
  • tools/morph_test_model.py
  • engine/include/kke/ai/BehaviorTree.h
  • engine/include/kke/Timeline.h
  • engine/include/kke/MusicDirector.h
  • engine/include/kke/Rollback.h
  • engine/include/kke/modules/ModelModule.h
  • engine/include/kke/Profiler.h
  • engine/include/kke/WorldStream.h
  • engine/include/kke/Weather.h
  • engine/include/kke/Localization.h
  • engine/include/kke/AnimCompress.h
  • engine/include/kke/FaceRig.h
  • engine/include/kke/Mocap.h
    ... and 52 more
## PR Reviewer Guide 🔍 <!-- pr-agent:review:full --> 📋 **Focus areas:** 🔴 12 open · ✅ 0 handled. Every push re-checks them; a handled area moves to *Resolved findings* at the bottom. <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 4 🔵🔵🔵🔵⚪</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> <tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ai/BehaviorTree.cpp#L303-L312'><strong>Potential Integer Overflow in Parallel Node</strong></a> In the `Parallel` node's `onTick` method, the code sums up success and failure counts from child nodes into `ok` and `bad` variables. If a behavior tree is constructed with a very large number of children (exceeding the maximum value of `size_t`), this summation could overflow, leading to incorrect status determination (e.g., reporting success when it should fail). While unlikely in typical use cases, robustness suggests using a type that can safely hold the sum or checking for potential overflow if the number of children is expected to be large. </summary> ```c++ size_t ok = 0, bad = 0; for (size_t i = 0; i < n; ++i) { if (m_done[i] == 0) { const BtStatus s = m_children[i]->tick(c); if (s == BtStatus::Success) m_done[i] = 1; else if (s == BtStatus::Failure) m_done[i] = 2; } ok += m_done[i] == 1; bad += m_done[i] == 2; } ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_music.cpp#L16-L16'><strong>Hard-coded Audio Sample Rate</strong></a> The test file defines a constant `kRate = 48000` for the audio sample rate. This hard-codes the test environment to a specific sample rate, which may not match the actual runtime configuration of the engine or other tests. If the engine supports configurable sample rates, these tests might fail or produce incorrect timing results on systems configured differently. It would be better to either use a default value from the engine's configuration or explicitly document why 48kHz is chosen as the test standard. </summary> ```c++ constexpr int kRate = 48000; ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Localization.cpp#L317-L331'><strong>Missing Error Handling for File I/O</strong></a> The `loadFile` function reads a file into a string stream and then passes it to `loadJson`. If the file is empty or contains only whitespace, `nlohmann::json::parse` will throw an exception. While the exception is caught, the error message returned to the caller will be "not JSON: [exception message]" instead of a more specific "file is empty" or similar. This makes debugging localization file issues harder. Additionally, if the file path is invalid, the error message is just "can't read <path>", which is fine, but the combination of these cases could lead to confusing logs. </summary> ```c++ bool Localization::loadFile(const std::string& path, std::string* error) { std::ifstream in(path, std::ios::binary); if (!in) { if (error) *error = "can't read " + path; return false; } std::stringstream text; text << in.rdbuf(); std::string inner; if (!loadJson(text.str(), &inner)) { if (error) *error = path + ": " + inner; return false; } return true; } ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_behavior_tree.cpp#L430-L441'><strong>Hardcoded Test Data in Source</strong></a> The test `ABossFightWithPhases` (line 416) uses a large JSON string literal directly in the source code. While this is common for small configs, it makes the test harder to read and maintain compared to loading from a separate file or using a helper function. It also increases the risk of typos in the JSON syntax within the C++ source. </summary> ```c++ const json spec = json::parse(R"({ "type": "reactive_selector", "label": "boss", "children": [ { "type": "reactive_sequence", "label": "phase 3", "children": [ { "type": "check", "key": "hp", "op": "<", "value": 30 }, { "type": "action", "name": "rage" } ] }, { "type": "reactive_sequence", "label": "phase 2", "children": [ { "type": "check", "key": "hp", "op": "<", "value": 60 }, { "type": "cooldown", "seconds": 5, "child": { "type": "action", "name": "summon" } }, "slam" ] }, { "type": "reactive_sequence", "label": "phase 1", "children": [ "playerNear", "charge" ] } ] })"); ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/tests/test_behavior_tree.cpp#L244-L255'><strong>Potential Integer Overflow in Random Sequence</strong></a> In the `RandomSequenceRunsEveryChildOnce` test (line 244), a `BehaviorTree` is created with a seed of `3`. The `btRandomSequence` constructor likely uses this seed to initialize a random number generator. If the implementation of `btRandomSequence` does not properly handle the seed or if the internal state machine relies on integer arithmetic that can overflow given the number of children (4 in this case), it might produce incorrect ordering or fail to visit all children. While unlikely with modern RNGs, it's worth noting that relying on a hardcoded small seed for a test that verifies randomness properties is slightly risky; a larger variety of seeds or a check against multiple runs would be more robust. </summary> ```c++ TEST(BehaviorTree, RandomSequenceRunsEveryChildOnce) { auto count = std::make_shared<int>(0); BtChildren c; for (int i = 0; i < 4; ++i) c.push_back(btAction([count](BtContext&) { ++*count; return S; })); BehaviorTree t(btRandomSequence(std::move(c)), 3); EXPECT_EQ(t.tick(0.1f), S); EXPECT_EQ(*count, 4); } ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Bidi.cpp#L299-L331'><strong>Missing Unicode Normalization</strong></a> The `decode` function in Bidi.cpp handles UTF-8 decoding but does not perform Unicode normalization. This can lead to inconsistent text representation when combining characters (like Arabic or Hebrew) are involved, potentially causing display issues or incorrect bidirectional algorithm behavior. For robust localization support, normalization should be applied before processing. </summary> ```c++ std::u32string decode(std::string_view s) { std::u32string out; out.reserve(s.size()); for (size_t i = 0; i < s.size();) { const unsigned char b = static_cast<unsigned char>(s[i]); int extra = 0; char32_t c = 0; if (b < 0x80) c = b; else if ((b & 0xE0) == 0xC0) { c = b & 0x1F; extra = 1; } else if ((b & 0xF0) == 0xE0) { c = b & 0x0F; extra = 2; } else if ((b & 0xF8) == 0xF0) { c = b & 0x07; extra = 3; } else { out.push_back(0xFFFD); ++i; continue; } if (i + static_cast<size_t>(extra) >= s.size() && extra > 0) { // cut off out.push_back(0xFFFD); ++i; continue; } bool ok = true; for (int k = 1; k <= extra; ++k) { const unsigned char cont = static_cast<unsigned char>(s[i + static_cast<size_t>(k)]); if ((cont & 0xC0) != 0x80) { ok = false; break; } c = (c << 6) | (cont & 0x3F); } if (!ok) { out.push_back(0xFFFD); ++i; continue; } out.push_back(c); i += static_cast<size_t>(extra) + 1; } return out; } ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Bidi.cpp#L287-L295'><strong>Incomplete Arabic Ligature Support</strong></a> The `lamAlef` function only handles specific alef characters (0x622, 0x623, 0x625, 0x627) for lam-alef ligatures. However, Arabic script includes other forms of alef that may require ligature handling depending on context. This incomplete coverage could result in incorrect shaping for certain Arabic words containing lam followed by different alef variants. </summary> ```c++ char32_t lamAlef(char32_t alef, bool final) { switch (alef) { case 0x622: return final ? 0xFEF6 : 0xFEF5; case 0x623: return final ? 0xFEF8 : 0xFEF7; case 0x625: return final ? 0xFEFA : 0xFEF9; case 0x627: return final ? 0xFEFC : 0xFEFB; default: return 0; } } ``` </details> <a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/ModelModule.cpp#L456-L457'><strong>Potential Resource Leak in Morph Parts Cleanup</strong></a><br>In the `remove` method, morph parts are retired using `std::make_shared<std::vector<MorphPart>>(std::move(inst.morphParts))`. While this moves the data, if `inst.morphParts` contains shared pointers to GPU buffers or other resources that need explicit cleanup beyond just moving the vector, there might be a leak. Ensure all underlying resources held by `MorphPart` are properly released when the instance is destroyed. <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Weather.cpp#L291-L291'><strong>Potential Integer Overflow</strong></a> In `Weather::update`, the wind speed calculation multiplies `s.wind` (up to 100) by `(1.0f + s.gust * n)`. Since `n` can reach approximately 1.0, the result can be around 200 m/s. While this is within float range, if `s.wind` or `s.gust` were to increase further in future modifications, or if the logic changes, this could lead to unrealistic physics or potential overflow in downstream calculations that expect reasonable wind speeds. The current bounds check for `wind` (0 to 100) is applied at definition time but not enforced during runtime updates where gusts are applied. </summary> ```c++ s.windSpeed = std::max(0.0f, s.wind * (1.0f + s.gust * n)); ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Weather.cpp#L227-L231'><strong>Missing Validation for Random Weather Parameters</strong></a> The `randomWeather` function accepts `minSeconds` and `maxSeconds` parameters but does not validate them against the hardcoded defaults or ensure they are positive. If a user passes negative values or zero, the logic `std::max(1.0f, std::isfinite(minSeconds) ? minSeconds : 120.0f)` might behave unexpectedly if `minSeconds` is negative (it would clamp to 1.0, which is fine, but the intent might be violated). More critically, if `minSeconds` is NaN or infinite, the behavior is undefined due to `std::isfinite` checks only handling finite numbers, potentially leading to infinite loops or incorrect timing if `m_hold` becomes invalid. </summary> ```c++ m_randomMin = std::max(1.0f, std::isfinite(minSeconds) ? minSeconds : 120.0f); m_randomMax = std::max(m_randomMin, std::isfinite(maxSeconds) ? maxSeconds : 300.0f); m_randomTransition = std::max(0.0f, std::isfinite(transition) ? transition : 40.0f); m_hold = static_cast<double>(m_randomMin + unit() * (m_randomMax - m_randomMin)); } ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/Mocap.cpp#L381-L390'><strong>Potential Out-of-Bounds Access in cleanMotion</strong></a> In `cleanMotion`, the loop `for (size_t i = 1; i + 1 < f.size(); ++i)` accesses `f[i - 1]`, `f[i]`, and `f[i + 1]`. This is safe as long as `f.size() >= 3`. However, the condition `if ((o.spikeDegrees > 0.0f || o.spikeMetres > 0.0f) && f.size() > 2)` ensures `f.size()` is at least 3 before entering the loop. But inside the smoothing section, there is a similar check `if (o.smoothSeconds > 0.0f && f.size() > 2)`. If `f.size()` is exactly 2, the loop `for (int i = 0; i < n; ++i)` where `n = static_cast<int>(f.size())` will run for `i=0` and `i=1`. Inside, it accesses `src[static_cast<size_t>(j)][b]` where `j` ranges from `-radius` to `radius`. If `radius` is greater than 0 (which it is if `sigma > 0`), then `j` can be negative or exceed `n-1`, causing out-of-bounds access on `src`. </summary> ```c++ for (int k = -radius; k <= radius; ++k) { const int j = i + k; if (j < 0 || j >= n) continue; const float w = weight[static_cast<size_t>(std::abs(k))]; const BoneTRS& x = src[static_cast<size_t>(j)][b]; t += w * x.t; s += w * x.s; const glm::quat r = glm::dot(ref, x.r) < 0.0f ? -x.r : x.r; q += w * glm::vec4(r.x, r.y, r.z, r.w); total += w; ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/142/head/engine/src/AnimCompress.cpp#L45-L46'><strong>Potential Division by Zero in reduceKeys</strong></a> In `reduceKeys`, the line `const float t = static_cast<float>(i - a) / static_cast<float>(b - a);` calculates a normalized time. If `a` and `b` are equal (which shouldn't happen given the stack logic `if (at - a > 1) stack.emplace_back(a, at); if (b - at > 1) stack.emplace_back(at, b);`), it would cause division by zero. However, the initial stack push `stack.emplace_back(0, n - 1)` ensures `b - a` is `n - 1`. If `n == 1`, the function returns early. If `n == 2`, the loop `for (size_t i = a + 1; i < b; ++i)` doesn't execute, so `worst` remains 0.0f, and no new keys are added. The division only happens if `i` iterates, implying `b - a > 1`. Thus, this is likely safe, but the comment "Ramer-Douglas-Peucker over time" suggests a standard algorithm where `a` and `b` are distinct indices. The code seems robust here, but it's worth noting that if `b - a` were somehow 0 due to a bug in stack management, it would crash. </summary> ```c++ const float t = static_cast<float>(i - a) / static_cast<float>(b - a); const float e = err(v[i], mix(v[a], v[b], t)); ``` </details> </td></tr> </table> <hr> ℹ️ **Chunked review:** the diff exceeded the model token budget, so it was reviewed in 8 chunks and the per-chunk results were merged. <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `engine/src/FaceRig.cpp` - `engine/src/ScriptWeather.cpp` - `tests/test_script_behavior.cpp` - `tests/test_script_music.cpp` - `tests/test_profiler.cpp` - `engine/src/WorldStream.cpp` - `engine/src/CrashReporter.cpp` - `tests/test_morph.cpp` - `engine/src/Application.cpp` - `engine/src/modules/ScriptLocale.cpp` - `tests/test_accessibility.cpp` - `tests/test_face_rig.cpp` - `engine/src/modules/GameShellModule.cpp` - `engine/src/modules/UiModule.cpp` - `tests/test_lod_chain.cpp` - `engine/src/EngineSettings.cpp` - `tests/test_crash_reporter.cpp` - `engine/src/Accessibility.cpp` - `engine/src/ScriptMusic.cpp` - `engine/src/AudioMixer.cpp` - `engine/src/modules/ScriptBindings.cpp` - `tests/test_bidi.cpp` - `engine/src/modules/ScriptProfile.cpp` - `engine/src/Log.cpp` - `engine/src/Animator.cpp` - `tests/test_engine_settings.cpp` - `engine/src/GltfAsset.cpp` - `engine/src/Morph.cpp` - `engine/src/modules/ScriptModule.cpp` - `engine/src/Subtitles.cpp` - `engine/src/Renderer.cpp` - `engine/src/LodChain.cpp` - `engine/src/modules/StatsModule.cpp` - `engine/src/modules/SettingsModule.cpp` - `engine/src/ModelAsset.cpp` - `engine/src/CameraDirector.cpp` - `shaders/tonemap_post.frag` - `tools/morph_test_model.py` - `engine/include/kke/ai/BehaviorTree.h` - `engine/include/kke/Timeline.h` - `engine/include/kke/MusicDirector.h` - `engine/include/kke/Rollback.h` - `engine/include/kke/modules/ModelModule.h` - `engine/include/kke/Profiler.h` - `engine/include/kke/WorldStream.h` - `engine/include/kke/Weather.h` - `engine/include/kke/Localization.h` - `engine/include/kke/AnimCompress.h` - `engine/include/kke/FaceRig.h` - `engine/include/kke/Mocap.h` ... and 52 more <!-- pr-agent-review-state:v1 {"findings":[{"body":"**Potential Integer Overflow in Random Sequence**\n\nIn the `RandomSequenceRunsEveryChildOnce` test (line 244), a `BehaviorTree` is created with a seed of `3`. The `btRandomSequence` constructor likely uses this seed to initialize a random number generator. If the implementation of `btRandomSequence` does not properly handle the seed or if the internal state machine relies on integer arithmetic that can overflow given the number of children (4 in this case), it might produce incorrect ordering or fail to visit all children. While unlikely with modern RNGs, it's worth noting that relying on a hardcoded small seed for a test that verifies randomness properties is slightly risky; a larger variety of seeds or a check against multiple runs would be more robust.","finding_id":"0a30f9a448df","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":255,"line_start":244,"path":"tests/test_behavior_tree.cpp","state":"ACTIVE"},{"body":"**Hardcoded Test Data in Source**\n\nThe test `ABossFightWithPhases` (line 416) uses a large JSON string literal directly in the source code. While this is common for small configs, it makes the test harder to read and maintain compared to loading from a separate file or using a helper function. It also increases the risk of typos in the JSON syntax within the C++ source.","finding_id":"3b0af2fc1115","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":441,"line_start":430,"path":"tests/test_behavior_tree.cpp","state":"ACTIVE"},{"body":"**Potential Integer Overflow**\n\nIn `Weather::update`, the wind speed calculation multiplies `s.wind` (up to 100) by `(1.0f + s.gust * n)`. Since `n` can reach approximately 1.0, the result can be around 200 m/s. While this is within float range, if `s.wind` or `s.gust` were to increase further in future modifications, or if the logic changes, this could lead to unrealistic physics or potential overflow in downstream calculations that expect reasonable wind speeds. The current bounds check for `wind` (0 to 100) is applied at definition time but not enforced during runtime updates where gusts are applied.","finding_id":"424b281affd2","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":291,"line_start":291,"path":"engine/src/Weather.cpp","state":"ACTIVE"},{"body":"**Incomplete Arabic Ligature Support**\n\nThe `lamAlef` function only handles specific alef characters (0x622, 0x623, 0x625, 0x627) for lam-alef ligatures. However, Arabic script includes other forms of alef that may require ligature handling depending on context. This incomplete coverage could result in incorrect shaping for certain Arabic words containing lam followed by different alef variants.","finding_id":"46c5bb9a7f23","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":295,"line_start":287,"path":"engine/src/Bidi.cpp","state":"ACTIVE"},{"body":"**Missing Validation for Random Weather Parameters**\n\nThe `randomWeather` function accepts `minSeconds` and `maxSeconds` parameters but does not validate them against the hardcoded defaults or ensure they are positive. If a user passes negative values or zero, the logic `std::max(1.0f, std::isfinite(minSeconds) ? minSeconds : 120.0f)` might behave unexpectedly if `minSeconds` is negative (it would clamp to 1.0, which is fine, but the intent might be violated). More critically, if `minSeconds` is NaN or infinite, the behavior is undefined due to `std::isfinite` checks only handling finite numbers, potentially leading to infinite loops or incorrect timing if `m_hold` becomes invalid.","finding_id":"4f28cbd4c8e7","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":231,"line_start":227,"path":"engine/src/Weather.cpp","state":"ACTIVE"},{"body":"**Missing Unicode Normalization**\n\nThe `decode` function in Bidi.cpp handles UTF-8 decoding but does not perform Unicode normalization. This can lead to inconsistent text representation when combining characters (like Arabic or Hebrew) are involved, potentially causing display issues or incorrect bidirectional algorithm behavior. For robust localization support, normalization should be applied before processing.","finding_id":"61fa183bdf51","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":331,"line_start":299,"path":"engine/src/Bidi.cpp","state":"ACTIVE"},{"body":"**Potential Resource Leak in Morph Parts Cleanup**\n\nIn the `remove` method, morph parts are retired using `std::make_shared<std::vector<MorphPart>>(std::move(inst.morphParts))`. While this moves the data, if `inst.morphParts` contains shared pointers to GPU buffers or other resources that need explicit cleanup beyond just moving the vector, there might be a leak. Ensure all underlying resources held by `MorphPart` are properly released when the instance is destroyed.","finding_id":"7af7cf51dfa1","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":457,"line_start":456,"path":"engine/src/ModelModule.cpp","state":"ACTIVE"},{"body":"**Potential Out-of-Bounds Access in cleanMotion**\n\nIn `cleanMotion`, the loop `for (size_t i = 1; i + 1 < f.size(); ++i)` accesses `f[i - 1]`, `f[i]`, and `f[i + 1]`. This is safe as long as `f.size() >= 3`. However, the condition `if ((o.spikeDegrees > 0.0f || o.spikeMetres > 0.0f) && f.size() > 2)` ensures `f.size()` is at least 3 before entering the loop. But inside the smoothing section, there is a similar check `if (o.smoothSeconds > 0.0f && f.size() > 2)`. If `f.size()` is exactly 2, the loop `for (int i = 0; i < n; ++i)` where `n = static_cast<int>(f.size())` will run for `i=0` and `i=1`. Inside, it accesses `src[static_cast<size_t>(j)][b]` where `j` ranges from `-radius` to `radius`. If `radius` is greater than 0 (which it is if `sigma > 0`), then `j` can be negative or exceed `n-1`, causing out-of-bounds access on `src`.","finding_id":"7f15e3331dff","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":390,"line_start":381,"path":"engine/src/Mocap.cpp","state":"ACTIVE"},{"body":"**Potential Division by Zero in reduceKeys**\n\nIn `reduceKeys`, the line `const float t = static_cast<float>(i - a) / static_cast<float>(b - a);` calculates a normalized time. If `a` and `b` are equal (which shouldn't happen given the stack logic `if (at - a > 1) stack.emplace_back(a, at); if (b - at > 1) stack.emplace_back(at, b);`), it would cause division by zero. However, the initial stack push `stack.emplace_back(0, n - 1)` ensures `b - a` is `n - 1`. If `n == 1`, the function returns early. If `n == 2`, the loop `for (size_t i = a + 1; i < b; ++i)` doesn't execute, so `worst` remains 0.0f, and no new keys are added. The division only happens if `i` iterates, implying `b - a > 1`. Thus, this is likely safe, but the comment \"Ramer-Douglas-Peucker over time\" suggests a standard algorithm where `a` and `b` are distinct indices. The code seems robust here, but it's worth noting that if `b - a` were somehow 0 due to a bug in stack management, it would crash.","finding_id":"8de3ff000714","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":46,"line_start":45,"path":"engine/src/AnimCompress.cpp","state":"ACTIVE"},{"body":"**Potential Integer Overflow in Parallel Node**\n\nIn the `Parallel` node's `onTick` method, the code sums up success and failure counts from child nodes into `ok` and `bad` variables. If a behavior tree is constructed with a very large number of children (exceeding the maximum value of `size_t`), this summation could overflow, leading to incorrect status determination (e.g., reporting success when it should fail). While unlikely in typical use cases, robustness suggests using a type that can safely hold the sum or checking for potential overflow if the number of children is expected to be large.","finding_id":"8fa7f843cfe0","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":312,"line_start":303,"path":"engine/src/ai/BehaviorTree.cpp","state":"ACTIVE"},{"body":"**Hard-coded Audio Sample Rate**\n\nThe test file defines a constant `kRate = 48000` for the audio sample rate. This hard-codes the test environment to a specific sample rate, which may not match the actual runtime configuration of the engine or other tests. If the engine supports configurable sample rates, these tests might fail or produce incorrect timing results on systems configured differently. It would be better to either use a default value from the engine's configuration or explicitly document why 48kHz is chosen as the test standard.","finding_id":"9668bee2eed2","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":16,"line_start":16,"path":"tests/test_music.cpp","state":"ACTIVE"},{"body":"**Missing Error Handling for File I/O**\n\nThe `loadFile` function reads a file into a string stream and then passes it to `loadJson`. If the file is empty or contains only whitespace, `nlohmann::json::parse` will throw an exception. While the exception is caught, the error message returned to the caller will be \"not JSON: [exception message]\" instead of a more specific \"file is empty\" or similar. This makes debugging localization file issues harder. Additionally, if the file path is invalid, the error message is just \"can't read <path>\", which is fine, but the combination of these cases could lead to confusing logs.","finding_id":"ea38fecb406c","first_seen":"2026-10-28T09:53:43.247368Z","last_seen":"2026-10-28T09:53:43.247368Z","last_seen_head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","line_end":331,"line_start":317,"path":"engine/src/Localization.cpp","state":"ACTIVE"}],"last_run":{"complete":false,"excluded_files":["ROADMAP.md","assets/fonts/NOTO-LICENSE.txt","assets/locales/ar.json","assets/locales/de.json","assets/locales/en.json","assets/locales/es.json","assets/locales/fr.json","assets/locales/he.json","assets/locales/nl.json","docs/ACCESSIBILITY.md","docs/AI.md","docs/AUDIO.md","docs/CRASH_REPORTS.md","docs/DEPENDENCIES.md","docs/FACIAL_ANIMATION.md","docs/GAPS.md","docs/LOCALIZATION.md","docs/LOD.md","docs/MOODS.md","docs/MOVEMENT.md","docs/NETWORKING.md","docs/PROCEDURAL_ANIMATION.md","docs/PROFILING.md","docs/README.md","docs/SCENES.md","docs/SCRIPTING.md","docs/TIMELINE.md","engine/CMakeLists.txt","engine/include/kke/Accessibility.h","engine/include/kke/AnimCompress.h","engine/include/kke/Animator.h","engine/include/kke/Application.h","engine/include/kke/AudioMixer.h","engine/include/kke/Bidi.h","engine/include/kke/CrashReporter.h","engine/include/kke/EngineSettings.h","engine/include/kke/FaceRig.h","engine/include/kke/Localization.h","engine/include/kke/LodChain.h","engine/include/kke/Log.h","engine/include/kke/Mesh.h","engine/include/kke/Mocap.h","engine/include/kke/ModelAsset.h","engine/include/kke/Morph.h","engine/include/kke/MusicDirector.h","engine/include/kke/Profiler.h","engine/include/kke/Renderer.h","engine/include/kke/Rollback.h","engine/include/kke/ScriptBehavior.h","engine/include/kke/ScriptMusic.h","engine/include/kke/ScriptTimeline.h","engine/include/kke/ScriptWeather.h","engine/include/kke/ScriptWorld.h","engine/include/kke/Subtitles.h","engine/include/kke/Timeline.h","engine/include/kke/Weather.h","engine/include/kke/WorldStream.h","engine/include/kke/ai/BehaviorTree.h","engine/include/kke/modules/GameShellModule.h","engine/include/kke/modules/ModelModule.h","engine/include/kke/modules/ScriptModule.h","engine/include/kke/modules/UiModule.h","engine/src/Accessibility.cpp","engine/src/Animator.cpp","engine/src/Application.cpp","engine/src/AudioMixer.cpp","engine/src/CameraDirector.cpp","engine/src/CrashReporter.cpp","engine/src/EngineSettings.cpp","engine/src/FaceRig.cpp","engine/src/GltfAsset.cpp","engine/src/LodChain.cpp","engine/src/Log.cpp","engine/src/ModelAsset.cpp","engine/src/Morph.cpp","engine/src/Renderer.cpp","engine/src/ScriptMusic.cpp","engine/src/ScriptWeather.cpp","engine/src/Subtitles.cpp","engine/src/WorldStream.cpp","engine/src/modules/GameShellModule.cpp","engine/src/modules/ScriptBindings.cpp","engine/src/modules/ScriptLocale.cpp","engine/src/modules/ScriptModule.cpp","engine/src/modules/ScriptProfile.cpp","engine/src/modules/SettingsModule.cpp","engine/src/modules/StatsModule.cpp","engine/src/modules/UiModule.cpp","mkdocs.yml","shaders/tonemap_post.frag","tests/CMakeLists.txt","tests/test_accessibility.cpp","tests/test_bidi.cpp","tests/test_crash_reporter.cpp","tests/test_engine_settings.cpp","tests/test_face_rig.cpp","tests/test_lod_chain.cpp","tests/test_morph.cpp","tests/test_profiler.cpp","tests/test_script_behavior.cpp","tests/test_script_music.cpp","tools/morph_test_model.py"],"head_sha":"1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89","kind":"partial","run_id":"https://git.kreative-kompas.com/khyretos/kk-engine/commit/1f1c0f0f2401f4a711e4b6c243183ce6adfc7c89"},"schema_version":1} -->
Collaborator

PR Code Suggestions ✨

No code suggestions found in the successfully analyzed chunks.

⚠️ Suggestion coverage: 102 file(s) were not analyzed because of the token budget or maximum chunk calls: engine/src/FaceRig.cpp, engine/src/ScriptWeather.cpp, tests/test_script_behavior.cpp, tests/test_script_music.cpp, tests/test_profiler.cpp, engine/src/WorldStream.cpp, engine/src/CrashReporter.cpp, tests/test_morph.cpp, engine/src/Application.cpp, engine/src/modules/ScriptLocale.cpp, tests/test_accessibility.cpp, tests/test_face_rig.cpp, engine/src/modules/GameShellModule.cpp, engine/src/modules/UiModule.cpp, tests/test_lod_chain.cpp, engine/src/EngineSettings.cpp, tests/test_crash_reporter.cpp, engine/src/Accessibility.cpp, engine/src/ScriptMusic.cpp, engine/src/AudioMixer.cpp, engine/src/modules/ScriptBindings.cpp, tests/test_bidi.cpp, engine/src/modules/ScriptProfile.cpp, engine/src/Log.cpp, engine/src/Animator.cpp, tests/test_engine_settings.cpp, engine/src/GltfAsset.cpp, engine/src/Morph.cpp, engine/src/modules/ScriptModule.cpp, engine/src/Subtitles.cpp, engine/src/Renderer.cpp, engine/src/LodChain.cpp, engine/src/modules/StatsModule.cpp, engine/src/modules/SettingsModule.cpp, engine/src/ModelAsset.cpp, engine/src/CameraDirector.cpp, shaders/tonemap_post.frag, tools/morph_test_model.py, engine/include/kke/ai/BehaviorTree.h, engine/include/kke/Timeline.h, engine/include/kke/MusicDirector.h, engine/include/kke/Rollback.h, engine/include/kke/modules/ModelModule.h, engine/include/kke/Profiler.h, engine/include/kke/WorldStream.h, engine/include/kke/Weather.h, engine/include/kke/Localization.h, engine/include/kke/AnimCompress.h, engine/include/kke/FaceRig.h, engine/include/kke/Mocap.h, and 52 more.

## PR Code Suggestions ✨ <!-- pr-agent:improve:no-suggestions --> No code suggestions found in the successfully analyzed chunks. ⚠️ **Suggestion coverage:** 102 file(s) were not analyzed because of the token budget or maximum chunk calls: `engine/src/FaceRig.cpp`, `engine/src/ScriptWeather.cpp`, `tests/test_script_behavior.cpp`, `tests/test_script_music.cpp`, `tests/test_profiler.cpp`, `engine/src/WorldStream.cpp`, `engine/src/CrashReporter.cpp`, `tests/test_morph.cpp`, `engine/src/Application.cpp`, `engine/src/modules/ScriptLocale.cpp`, `tests/test_accessibility.cpp`, `tests/test_face_rig.cpp`, `engine/src/modules/GameShellModule.cpp`, `engine/src/modules/UiModule.cpp`, `tests/test_lod_chain.cpp`, `engine/src/EngineSettings.cpp`, `tests/test_crash_reporter.cpp`, `engine/src/Accessibility.cpp`, `engine/src/ScriptMusic.cpp`, `engine/src/AudioMixer.cpp`, `engine/src/modules/ScriptBindings.cpp`, `tests/test_bidi.cpp`, `engine/src/modules/ScriptProfile.cpp`, `engine/src/Log.cpp`, `engine/src/Animator.cpp`, `tests/test_engine_settings.cpp`, `engine/src/GltfAsset.cpp`, `engine/src/Morph.cpp`, `engine/src/modules/ScriptModule.cpp`, `engine/src/Subtitles.cpp`, `engine/src/Renderer.cpp`, `engine/src/LodChain.cpp`, `engine/src/modules/StatsModule.cpp`, `engine/src/modules/SettingsModule.cpp`, `engine/src/ModelAsset.cpp`, `engine/src/CameraDirector.cpp`, `shaders/tonemap_post.frag`, `tools/morph_test_model.py`, `engine/include/kke/ai/BehaviorTree.h`, `engine/include/kke/Timeline.h`, `engine/include/kke/MusicDirector.h`, `engine/include/kke/Rollback.h`, `engine/include/kke/modules/ModelModule.h`, `engine/include/kke/Profiler.h`, `engine/include/kke/WorldStream.h`, `engine/include/kke/Weather.h`, `engine/include/kke/Localization.h`, `engine/include/kke/AnimCompress.h`, `engine/include/kke/FaceRig.h`, `engine/include/kke/Mocap.h`, and 52 more.

🧹 Lint report · ❌ 44 problems in 6 checks · 🧹 3 formatting checks to tidy

Nothing here blocks the merge (report only).

Check Errors Warnings Files
❌ MARKDOWN · markdownlint 28 0 261
❌ CPP · cppcheck 11 0 1009
❌ YAML · yamllint 2 0 57
❌ BASH · shellcheck 1 0 11
❌ DOCKERFILE · hadolint 1 0 9
❌ REPOSITORY · trivy 1 0 –
MARKDOWN · markdownlint: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/markdown_markdownlint/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
docs/ANDROID.md:87 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:128 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:136 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:149 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:163 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:186 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ASSETS.md:155 error MD046/code-block-style Code block style [Expected: fenced; Actual: indented]
docs/CRASH_REPORTS.md:9 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:21 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: " Kreative Kompas Engine (KKE)"]
docs/HISTORY.md:520 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:727 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:734 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:2031 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:2090 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/LOD.md:7 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/LOD.md:9 error MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]
docs/MODDING.md:13 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/NETWORKING.md:114 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/PLATFORMS.md:48 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/PROCEDURAL_ANIMATION.md:36 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/PROCEDURAL_ANIMATION.md:284 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
…
CPP · cppcheck: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/cpp_cppcheck/
-----------------------------------------------
✅ [SUCCESS] benchmarks/kke_bench/main.cpp
✅ [SUCCESS] benchmarks/kke_benchmark/main.cpp
✅ [SUCCESS] engine/include/engine/Buffer.h
✅ [SUCCESS] engine/include/engine/Mesh.h
✅ [SUCCESS] engine/include/engine/Pipeline.h
✅ [SUCCESS] engine/include/engine/Renderer.h
✅ [SUCCESS] engine/include/engine/SwapChain.h
✅ [SUCCESS] engine/include/engine/VulkanCheck.h
✅ [SUCCESS] engine/include/engine/VulkanDevice.h
✅ [SUCCESS] engine/include/engine/Window.h
✅ [SUCCESS] engine/include/kke/Accessibility.h
✅ [SUCCESS] engine/include/kke/AnimCompress.h
✅ [SUCCESS] engine/include/kke/AnimEvents.h
✅ [SUCCESS] engine/include/kke/AnimRig.h
✅ [SUCCESS] engine/include/kke/Animator.h
✅ [SUCCESS] engine/include/kke/Application.h
✅ [SUCCESS] engine/include/kke/AssetCatalog.h
✅ [SUCCESS] engine/include/kke/AudioMixer.h
✅ [SUCCESS] engine/include/kke/AutoRig.h
✅ [SUCCESS] engine/include/kke/BenchRecorder.h
✅ [SUCCESS] engine/include/kke/BenchmarkReport.h
✅ [SUCCESS] engine/include/kke/Bidi.h
✅ [SUCCESS] engine/include/kke/Bloom.h
…
YAML · yamllint: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/yaml_yamllint/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
games/goblin_horde/data/heroes.yml
  53:11     error    too many spaces after colon  (colons)
  63:11     error    too many spaces after colon  (colons)
BASH · shellcheck: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/bash_shellcheck/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
In tools/packaging/package.sh line 130:
	done < <(find "$stage" "$stage/benchmark" -maxdepth 1 -type f \( -name "*$exe" -o -name '*.dll' \) $([ -z "$exe" ] && echo "-perm -u+x") -print0)
                                                                                                           ^-- SC2046 (warning): Quote this to prevent word splitting.
For more information:
  https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt...
DOCKERFILE · hadolint: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/dockerfile_hadolint/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
docker/android.Dockerfile:19 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/appimage.Dockerfile:38 SC2016 info: Expressions don't expand in single quotes, use double quotes for that.
docker/appimage.Dockerfile:51 DL3003 warning: Use WORKDIR to switch to a directory
docker/appimage.Dockerfile:57 DL3003 warning: Use WORKDIR to switch to a directory
docker/appimage.Dockerfile:63 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/appimage.Dockerfile:63 DL3003 warning: Use WORKDIR to switch to a directory
docker/appimage.Dockerfile:73 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/appimage.Dockerfile:73 DL3003 warning: Use WORKDIR to switch to a directory
docker/ci.Dockerfile:42 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/ci.Dockerfile:56 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/server/Dockerfile:22 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/vm.Dockerfile:22 SC2086 info: Double quote to prevent globbing and word splitting.
docker/vm.Dockerfile:29 DL3003 warning: Use WORKDIR to switch to a directory
docker/vm.Dockerfile:29 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
website/Dockerfile:9 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
REPOSITORY · trivy: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/repository_trivy/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
2026-10-28T10:05:04Z	INFO	[vulndb] Need to update DB
2026-10-28T10:05:04Z	INFO	[vulndb] Downloading vulnerability DB...
2026-10-28T10:05:04Z	INFO	[vulndb] Downloading artifact...	repo="mirror.gcr.io/aquasec/trivy-db:2"
6.05 MiB / 130.06 MiB [-->___________________________________________________________] 4.65% ? p/s ?14.45 MiB / 130.06 MiB [------>_____________________________________________________] 11.11% ? p/s ?24.54 MiB / 130.06 MiB [----------->________________________________________________] 18.87% ? p/s ?33.58 MiB / 130.06 MiB [------------>__________________________________] 25.82% 45.74 MiB p/s ETA 2s44.08 MiB / 130.06 MiB [--------------->_______________________________] 33.90% 45.74 MiB p/s ETA 1s50.37 MiB / 130.06 MiB [------------------>____________________________] 38.73% 45.74 MiB p/s ETA 1s56.32 MiB / 130.06 MiB [-------------------->__________________________] 43.30% 45.21 MiB p/s ETA 1s66.88 MiB / 130.06 MiB [------------------------>______________________] 51.43% 45.21 MiB p/s ETA 1s75.20 MiB / 130.06 MiB [--------------------------->___________________] 57.82% 45.21 MiB p/s ETA 1s85.10 MiB / 130.06 MiB [------------------------------>________________] 65.44% 45.40 MiB p/s ETA 0s93.01 MiB / 130.06 MiB [--------------------------------->_____________] 71.52% 45.40 MiB p/s ETA 0s104.81 MiB / 130.06 MiB [------------------------------------->________] 80.59% 45.40 MiB p/s ETA 0s111.86 MiB / 130.06 MiB [--------------------------------------->______] 86.01% 45.37 MiB p/s ETA 0s122.36 MiB / 130.06 MiB [------------------------------------------->__] 94.08% 45.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 45.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 44.39 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 44.39 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 44.39 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 41.53 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 41.53 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 41.53 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 38.85 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 38.85 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 38.85 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 36.34 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 36.34 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 36.34 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 34.00 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 34.00 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 34.00 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 31.81 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 31.81 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 31.81 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 29.75 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 29.75 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 29.75 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 27.83 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 27.83 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 27.83 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 26.04 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 26.04 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 26.04 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 24.36 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 24.36 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 24.36 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 22.79 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 22.79 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 22.79 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 21.32 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 21.32 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 21.32 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 19.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 19.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 19.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 18.65 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 18.65 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 18.65 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 17.45 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 17.45 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 17.45 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 16.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 16.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 16.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 15.27 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 15.27 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 15.27 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 14.29 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 14.29 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 14.29 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 13.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 13.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 13.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 12.50 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 12.50 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 12.50 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 11.70 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 11.70 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 11.70 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.24 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.24 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.24 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 9.58 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 9.58 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 9.58 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.96 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.96 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.96 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.38 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.38 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.38 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.84 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.84 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.84 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------------] 100.00% 6.70 MiB p/s 20s2026-10-28T10:05:24Z	INFO	[vulndb] Artifact successfully downloaded	repo="mirror.gcr.io/aquasec/trivy-db:2"
2026-10-28T10:05:24Z	INFO	[vuln] Vulnerability scanning is enabled
2026-10-28T10:05:24Z	INFO	[misconfig] Misconfiguration scanning is enabled
2026-10-28T10:05:24Z	INFO	[checks-client] Need to update the checks bundle
2026-10-28T10:05:24Z	INFO	[checks-client] Downloading the checks bundle...
0 B / 234.57 KiB [___________________________________________________________________] 0.00% ? p/s ?234.57 KiB / 234.57 KiB [--------------------------------------------------------->] 100.00% ? p/s ?234.57 KiB / 234.57 KiB [--------------------------------------------------------->] 100.00% ? p/s ?234.57 KiB / 234.57 KiB [------------------------------------------->] 100.00% 390.36 KiB p/s ETA 0s234.57 KiB / 234.57 KiB [---------------------------------------------] 100.00% 386.23 KiB p/s 800ms2026-10-28T10:05:28Z	WARN	[rego] Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module	file_path="root/.cache/trivy/policy/content/policies/cloud/policies/aws/cloudfront/enable_logging.rego" minimum_trivy_version="0.72.0"
2026-10-28T10:05:29Z	ERROR	[dockerfile scanner] Failed to parse file	file_path="docker/server/Dockerfile.dockerignore" err="parse dockerfile instruction: parse instruction \".git\": unknown instruction: .git"
2026-10-28T10:05:29Z	INFO	Number of language-specific files	num=0
2026-10-28T10:05:29Z	INFO	Detected config files	num=9
Report Summary
┌──────────────────────────────┬────────────┬─────────────────┬───────────────────┐
│            Target            │    Type    │ Vulnerabilities │ Misconfigurations │
├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ benchmarks/docker/Dockerfile │ dockerfile │        -        │         2         │
├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ docker/android.Dockerfile    │ dockerfile │        -        │         2         │
├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ docker/appimage.Dockerfile   │ dockerfile │        -        │         5         │
…

🧹 Formatting only (layout, no behaviour; the nightly lint-fix job applies these as a PR): YAML · prettier (57 files checked), BASH · shfmt (11 files checked), JSON · prettier (154 files checked)

✅ Clean: actionlint, git_diff, gitleaks, grype, jsonlint, secretlint, stylelint, trufflehog, xmllint

MegaLinter on soucouyant · full logs in the Actions run · comment /improve for AI fix suggestions

<!-- kk-lint --> ## 🧹 Lint report · ❌ 44 problems in 6 checks · 🧹 3 formatting checks to tidy Nothing here blocks the merge (report only). | | Check | Errors | Warnings | Files | |---|---|---:|---:|---:| | ❌ | MARKDOWN · markdownlint | 28 | 0 | 261 | | ❌ | CPP · cppcheck | 11 | 0 | 1009 | | ❌ | YAML · yamllint | 2 | 0 | 57 | | ❌ | BASH · shellcheck | 1 | 0 | 11 | | ❌ | DOCKERFILE · hadolint | 1 | 0 | 9 | | ❌ | REPOSITORY · trivy | 1 | 0 | – | <details><summary>MARKDOWN · markdownlint: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/markdown_markdownlint/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: docs/ANDROID.md:87 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:128 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:136 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:149 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:163 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:186 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ASSETS.md:155 error MD046/code-block-style Code block style [Expected: fenced; Actual: indented] docs/CRASH_REPORTS.md:9 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:21 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: " Kreative Kompas Engine (KKE)"] docs/HISTORY.md:520 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:727 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:734 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:2031 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:2090 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/LOD.md:7 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/LOD.md:9 error MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"] docs/MODDING.md:13 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/NETWORKING.md:114 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/PLATFORMS.md:48 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/PROCEDURAL_ANIMATION.md:36 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/PROCEDURAL_ANIMATION.md:284 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] … ``` </details> <details><summary>CPP · cppcheck: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/cpp_cppcheck/ ----------------------------------------------- ✅ [SUCCESS] benchmarks/kke_bench/main.cpp ✅ [SUCCESS] benchmarks/kke_benchmark/main.cpp ✅ [SUCCESS] engine/include/engine/Buffer.h ✅ [SUCCESS] engine/include/engine/Mesh.h ✅ [SUCCESS] engine/include/engine/Pipeline.h ✅ [SUCCESS] engine/include/engine/Renderer.h ✅ [SUCCESS] engine/include/engine/SwapChain.h ✅ [SUCCESS] engine/include/engine/VulkanCheck.h ✅ [SUCCESS] engine/include/engine/VulkanDevice.h ✅ [SUCCESS] engine/include/engine/Window.h ✅ [SUCCESS] engine/include/kke/Accessibility.h ✅ [SUCCESS] engine/include/kke/AnimCompress.h ✅ [SUCCESS] engine/include/kke/AnimEvents.h ✅ [SUCCESS] engine/include/kke/AnimRig.h ✅ [SUCCESS] engine/include/kke/Animator.h ✅ [SUCCESS] engine/include/kke/Application.h ✅ [SUCCESS] engine/include/kke/AssetCatalog.h ✅ [SUCCESS] engine/include/kke/AudioMixer.h ✅ [SUCCESS] engine/include/kke/AutoRig.h ✅ [SUCCESS] engine/include/kke/BenchRecorder.h ✅ [SUCCESS] engine/include/kke/BenchmarkReport.h ✅ [SUCCESS] engine/include/kke/Bidi.h ✅ [SUCCESS] engine/include/kke/Bloom.h … ``` </details> <details><summary>YAML · yamllint: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/yaml_yamllint/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: games/goblin_horde/data/heroes.yml 53:11 error too many spaces after colon (colons) 63:11 error too many spaces after colon (colons) ``` </details> <details><summary>BASH · shellcheck: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/bash_shellcheck/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: In tools/packaging/package.sh line 130: done < <(find "$stage" "$stage/benchmark" -maxdepth 1 -type f \( -name "*$exe" -o -name '*.dll' \) $([ -z "$exe" ] && echo "-perm -u+x") -print0) ^-- SC2046 (warning): Quote this to prevent word splitting. For more information: https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt... ``` </details> <details><summary>DOCKERFILE · hadolint: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/dockerfile_hadolint/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: docker/android.Dockerfile:19 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/appimage.Dockerfile:38 SC2016 info: Expressions don't expand in single quotes, use double quotes for that. docker/appimage.Dockerfile:51 DL3003 warning: Use WORKDIR to switch to a directory docker/appimage.Dockerfile:57 DL3003 warning: Use WORKDIR to switch to a directory docker/appimage.Dockerfile:63 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/appimage.Dockerfile:63 DL3003 warning: Use WORKDIR to switch to a directory docker/appimage.Dockerfile:73 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/appimage.Dockerfile:73 DL3003 warning: Use WORKDIR to switch to a directory docker/ci.Dockerfile:42 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/ci.Dockerfile:56 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/server/Dockerfile:22 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/vm.Dockerfile:22 SC2086 info: Double quote to prevent globbing and word splitting. docker/vm.Dockerfile:29 DL3003 warning: Use WORKDIR to switch to a directory docker/vm.Dockerfile:29 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check website/Dockerfile:9 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check ``` </details> <details><summary>REPOSITORY · trivy: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/repository_trivy/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: 2026-10-28T10:05:04Z INFO [vulndb] Need to update DB 2026-10-28T10:05:04Z INFO [vulndb] Downloading vulnerability DB... 2026-10-28T10:05:04Z INFO [vulndb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-db:2" 6.05 MiB / 130.06 MiB [-->___________________________________________________________] 4.65% ? p/s ?14.45 MiB / 130.06 MiB [------>_____________________________________________________] 11.11% ? p/s ?24.54 MiB / 130.06 MiB [----------->________________________________________________] 18.87% ? p/s ?33.58 MiB / 130.06 MiB [------------>__________________________________] 25.82% 45.74 MiB p/s ETA 2s44.08 MiB / 130.06 MiB [--------------->_______________________________] 33.90% 45.74 MiB p/s ETA 1s50.37 MiB / 130.06 MiB [------------------>____________________________] 38.73% 45.74 MiB p/s ETA 1s56.32 MiB / 130.06 MiB [-------------------->__________________________] 43.30% 45.21 MiB p/s ETA 1s66.88 MiB / 130.06 MiB [------------------------>______________________] 51.43% 45.21 MiB p/s ETA 1s75.20 MiB / 130.06 MiB [--------------------------->___________________] 57.82% 45.21 MiB p/s ETA 1s85.10 MiB / 130.06 MiB [------------------------------>________________] 65.44% 45.40 MiB p/s ETA 0s93.01 MiB / 130.06 MiB [--------------------------------->_____________] 71.52% 45.40 MiB p/s ETA 0s104.81 MiB / 130.06 MiB [------------------------------------->________] 80.59% 45.40 MiB p/s ETA 0s111.86 MiB / 130.06 MiB [--------------------------------------->______] 86.01% 45.37 MiB p/s ETA 0s122.36 MiB / 130.06 MiB [------------------------------------------->__] 94.08% 45.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 45.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 44.39 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 44.39 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 44.39 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 41.53 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 41.53 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 41.53 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 38.85 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 38.85 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 38.85 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 36.34 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 36.34 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 36.34 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 34.00 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 34.00 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 34.00 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 31.81 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 31.81 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 31.81 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 29.75 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 29.75 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 29.75 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 27.83 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 27.83 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 27.83 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 26.04 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 26.04 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 26.04 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 24.36 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 24.36 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 24.36 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 22.79 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 22.79 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 22.79 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 21.32 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 21.32 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 21.32 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 19.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 19.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 19.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 18.65 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 18.65 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 18.65 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 17.45 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 17.45 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 17.45 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 16.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 16.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 16.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 15.27 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 15.27 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 15.27 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 14.29 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 14.29 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 14.29 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 13.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 13.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 13.37 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 12.50 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 12.50 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 12.50 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 11.70 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 11.70 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 11.70 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.94 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.24 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.24 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------->] 100.00% 10.24 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 9.58 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 9.58 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 9.58 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.96 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.96 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.96 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.38 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.38 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 8.38 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.84 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.84 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.84 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [--------------------------------------------->] 100.00% 7.33 MiB p/s ETA 0s130.06 MiB / 130.06 MiB [-------------------------------------------------] 100.00% 6.70 MiB p/s 20s2026-10-28T10:05:24Z INFO [vulndb] Artifact successfully downloaded repo="mirror.gcr.io/aquasec/trivy-db:2" 2026-10-28T10:05:24Z INFO [vuln] Vulnerability scanning is enabled 2026-10-28T10:05:24Z INFO [misconfig] Misconfiguration scanning is enabled 2026-10-28T10:05:24Z INFO [checks-client] Need to update the checks bundle 2026-10-28T10:05:24Z INFO [checks-client] Downloading the checks bundle... 0 B / 234.57 KiB [___________________________________________________________________] 0.00% ? p/s ?234.57 KiB / 234.57 KiB [--------------------------------------------------------->] 100.00% ? p/s ?234.57 KiB / 234.57 KiB [--------------------------------------------------------->] 100.00% ? p/s ?234.57 KiB / 234.57 KiB [------------------------------------------->] 100.00% 390.36 KiB p/s ETA 0s234.57 KiB / 234.57 KiB [---------------------------------------------] 100.00% 386.23 KiB p/s 800ms2026-10-28T10:05:28Z WARN [rego] Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module file_path="root/.cache/trivy/policy/content/policies/cloud/policies/aws/cloudfront/enable_logging.rego" minimum_trivy_version="0.72.0" 2026-10-28T10:05:29Z ERROR [dockerfile scanner] Failed to parse file file_path="docker/server/Dockerfile.dockerignore" err="parse dockerfile instruction: parse instruction \".git\": unknown instruction: .git" 2026-10-28T10:05:29Z INFO Number of language-specific files num=0 2026-10-28T10:05:29Z INFO Detected config files num=9 Report Summary ┌──────────────────────────────┬────────────┬─────────────────┬───────────────────┐ │ Target │ Type │ Vulnerabilities │ Misconfigurations │ ├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤ │ benchmarks/docker/Dockerfile │ dockerfile │ - │ 2 │ ├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤ │ docker/android.Dockerfile │ dockerfile │ - │ 2 │ ├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤ │ docker/appimage.Dockerfile │ dockerfile │ - │ 5 │ … ``` </details> 🧹 Formatting only (layout, no behaviour; the nightly lint-fix job applies these as a PR): YAML · prettier (57 files checked), BASH · shfmt (11 files checked), JSON · prettier (154 files checked) ✅ Clean: actionlint, git_diff, gitleaks, grype, jsonlint, secretlint, stylelint, trufflehog, xmllint <sub>MegaLinter on soucouyant · full logs in the Actions run · comment <code>/improve</code> for AI fix suggestions</sub>
Some checks are pending
CI / dependencies (pull_request) Successful in 6s
Required
Details
CI / std-includes (pull_request) Successful in 10s
Required
Details
Benchmarks / benchmarks (pull_request) Successful in 4m56s
Required
Details
CI / shipping-build (pull_request) Successful in 13m57s
Required
Details
Docs site / site (pull_request) Successful in 21s
Required
Details
lint / megalinter (pull_request) Successful in 3m28s
Required
Details
CI / android (pull_request) Failing after 9m49s
Required
Details
CI / release-build (pull_request) Successful in 32m49s
Required
Details
Windows (native) / build (pull_request) Failing after 23m46s
Required
Details
Windows (native) / test (pull_request) Has been skipped
Required
Details
CI / build-test-and-coverage (pull_request) Successful in 42m8s
Required
Details
Windows (native) / publish (pull_request) Has been skipped
review / pr-agent
Required
kk / all-checks
Required
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/142/head:khyretos/claude/engine-parity
git switch khyretos/claude/engine-parity
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
khyretos/kk-engine!142
No description provided.