WIP: Character creator: a wardrobe of a hundred garments in the body's own fabrics #136

Draft
khyretos wants to merge 1 commit from khyretos/claude/creator-wardrobe into claude/creator-anime AGit
Owner

Requested by Kees · project thread

Stacked on the anime PR #135 (base claude/creator-anime), which stacks on the clothes PR #134. One commit. Merge order: #134, #135, then this.

WIP: visual work. Kees's look-OK needed before merging; then drop the WIP prefix.

Kees (10-27): "since we have our own cloth and physics simulations, make some base shirts, pants, shoes etc, at least like 100 items, to render them in game so I can test the character creator fully."

What

Eleven garments were each a hand-written cut of MakeHuman's body suit. Now every garment is a line in a table (HumanClothes.cpp): a cut (sleeve, neckline, hem, length), a layer offset, a clothFabric() preset and its own colour. 113 garments in seven slots: tops (32), outerwear (20), bottoms (15), skirts (12), dresses (12), socks (6), shoes (11), gloves (5). Adding one is adding a line.

  • garmentPieces(): a dress is a bodice from the suit and a skirt of cloth; a long coat, trench, duster, kimono or cape is a jacket and its tails. garmentShape() stays as the first piece.
  • Gloves are cut from the body's own hands (the body group), the only pieces not from the suit.
  • The fabric drives the cloth simulation (satin drapes, denim and leather hold, a tulle skirt is net) and the fitted pieces' material (garmentMaterial: roughness and gloss).
  • Slots: garmentSlots(), garmentSlotsClash() (a dress takes the top's and the bottom's place). The creator's wear(), random outfits (dress 25%, coat 30%, gloves 15%), the self-test (counts pieces), the Clothes tab (seven sections) and the colour-slot buttons follow. dressUp() logs what it fitted: clothes: 6 piece(s), 2 as cloth: slip dress, trench coat, opera gloves, thigh boots.
  • Share codes are unchanged (a garment travels as its id and colour); the eleven old ids still exist.
  • docs/HUMANS.md "Clothes" rewritten; creator README.

Still plain colours with the fabric's weave and sheen, no printed textures (MakeHuman's .mhclo clothes remain the next step for that).

Tests

  • HumanClothes.EveryGarmentHasItsPart: >= 100 garments, every piece has vertices, a known fabric and slot; necklines and sleeves take cloth away (V < crew, deep V < V, tank < tee < three-quarter < long); dresses are two pieces; slot clashes.
  • HumanClothes.DressedBakeIsOneSkinnedModel: a dress gives two meshes, gloves a skinned one from the skin; satin shinier than wool.
  • Full kke_tests: 1346 passed.
  • check_dependencies, check_game_modules, check_std_includes.
  • Creator self-test under Xvfb, twice: random people in a sweater dress / socks / ankle boots and in a henley, duster, denim skirt, socks, ankle boots, opera gloves: passed.
  • Five fixed outfits under Xvfb in benchmark mode (writes screenshots): slip dress + trench coat + opera gloves + thigh boots; hoodie + jeans + high-tops + fingerless gloves (male); linen shirt + blazer + trousers + loafers (male); ball gown + long gloves + heels (Curvy, turning); camisole + kimono + hakama + sandals (turning). All fitted, no warnings. Screenshots in the handoff folder files/shots/ (I could not view them myself: Kees's look is needed).

🤖 Generated with Claude Code

<!-- claude-projects-attribution: {"github_login":"Khyretos"} --> _Requested by **Kees** · [project thread](https://claude.ai/code/project/chan_01Ds4PKvSLCNCCKr5TJxrV2A?thread=cmsg_01Ds4PKvSLCNCCKr5TJxrV2ABb2WaYyLkc7eUMNFVK1p2Z)_ Stacked on the anime PR #135 (base `claude/creator-anime`), which stacks on the clothes PR #134. One commit. Merge order: #134, #135, then this. **WIP: visual work. Kees's look-OK needed before merging; then drop the WIP prefix.** Kees (10-27): "since we have our own cloth and physics simulations, make some base shirts, pants, shoes etc, at least like 100 items, to render them in game so I can test the character creator fully." ## What Eleven garments were each a hand-written cut of MakeHuman's body suit. Now every garment is a line in a table (`HumanClothes.cpp`): a cut (sleeve, neckline, hem, length), a layer offset, a `clothFabric()` preset and its own colour. **113 garments in seven slots**: tops (32), outerwear (20), bottoms (15), skirts (12), dresses (12), socks (6), shoes (11), gloves (5). Adding one is adding a line. - `garmentPieces()`: a dress is a bodice from the suit and a skirt of cloth; a long coat, trench, duster, kimono or cape is a jacket and its tails. `garmentShape()` stays as the first piece. - Gloves are cut from the body's own hands (the `body` group), the only pieces not from the suit. - The fabric drives the cloth simulation (satin drapes, denim and leather hold, a tulle skirt is net) and the fitted pieces' material (`garmentMaterial`: roughness and gloss). - Slots: `garmentSlots()`, `garmentSlotsClash()` (a dress takes the top's and the bottom's place). The creator's `wear()`, random outfits (dress 25%, coat 30%, gloves 15%), the self-test (counts pieces), the Clothes tab (seven sections) and the colour-slot buttons follow. `dressUp()` logs what it fitted: `clothes: 6 piece(s), 2 as cloth: slip dress, trench coat, opera gloves, thigh boots`. - Share codes are unchanged (a garment travels as its id and colour); the eleven old ids still exist. - docs/HUMANS.md "Clothes" rewritten; creator README. Still plain colours with the fabric's weave and sheen, no printed textures (MakeHuman's `.mhclo` clothes remain the next step for that). ## Tests - [x] `HumanClothes.EveryGarmentHasItsPart`: >= 100 garments, every piece has vertices, a known fabric and slot; necklines and sleeves take cloth away (V < crew, deep V < V, tank < tee < three-quarter < long); dresses are two pieces; slot clashes. - [x] `HumanClothes.DressedBakeIsOneSkinnedModel`: a dress gives two meshes, gloves a skinned one from the skin; satin shinier than wool. - [x] Full `kke_tests`: 1346 passed. - [x] check_dependencies, check_game_modules, check_std_includes. - [x] Creator self-test under Xvfb, twice: random people in a sweater dress / socks / ankle boots and in a henley, duster, denim skirt, socks, ankle boots, opera gloves: passed. - [x] Five fixed outfits under Xvfb in benchmark mode (writes screenshots): slip dress + trench coat + opera gloves + thigh boots; hoodie + jeans + high-tops + fingerless gloves (male); linen shirt + blazer + trousers + loafers (male); ball gown + long gloves + heels (Curvy, turning); camisole + kimono + hakama + sandals (turning). All fitted, no warnings. Screenshots in the handoff folder `files/shots/` (I could not view them myself: Kees's look is needed). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Character creator: a wardrobe of a hundred garments in the body's own fabrics
All checks were successful
CI / dependencies (pull_request) Successful in 5s
CI / std-includes (pull_request) Successful in 14s
CI / shipping-build (pull_request) Successful in 5m26s
CI / release-build (pull_request) Successful in 7m40s
CI / android (pull_request) Successful in 5m27s
Docs site / site (pull_request) Successful in 11s
lint / megalinter (pull_request) Successful in 59s
Benchmarks / benchmarks (pull_request) Successful in 25m43s
Windows (native) / build (pull_request) Successful in 9m30s
CI / build-test-and-coverage (pull_request) Successful in 1h11m36s
Windows (native) / test (pull_request) Successful in 6m12s
Windows (native) / publish (pull_request) Has been skipped
31ac0f726d
Eleven garments were each a hand-written cut of the body suit. Kees wants
enough clothes to test the creator properly: a hundred and more, on the
engine's own cloth and fabrics.

- Every garment is now a line in a table (HumanClothes.cpp): a cut
  (sleeve, neckline, hem, length), a layer offset, a clothFabric() preset
  and its own colour. 113 garments in seven slots: tops (32), outerwear
  (20), bottoms (15), skirts (12), dresses (12), socks (6), shoes (11),
  gloves (5).
- garmentPieces(): a dress is a bodice from the suit and a skirt of
  cloth; a long coat, trench, duster, kimono or cape a jacket and its
  tails. garmentShape() stays as the first piece.
- Gloves are cut from the body's own hands (the "body" group), the only
  pieces not from the suit.
- The fabric drives the cloth simulation (satin drapes, denim and
  leather hold, tulle is net) and the fitted pieces' material
  (garmentMaterial: roughness and gloss).
- Slots: garmentSlots(), garmentSlotsClash() (a dress takes the top's
  and the bottom's place). The creator's wear(), random outfits, the
  self-test (counts pieces), the Clothes tab (seven sections) and the
  colour slot buttons follow. dressUp() logs what it fitted.
- Tests: >= 100 garments, every piece has vertices and a known fabric
  and slot, necklines and sleeves take cloth away, dresses are two
  pieces, dress() gives a dress two meshes and gloves a skinned one.
  docs/HUMANS.md "Clothes" rewritten.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator

Title

WIP: Character creator: a wardrobe of a hundred garments in the body's own fabrics


User description

Requested by Kees · project thread

Stacked on the anime PR #135 (base claude/creator-anime), which stacks on the clothes PR #134. One commit. Merge order: #134, #135, then this.

WIP: visual work. Kees's look-OK needed before merging; then drop the WIP prefix.

Kees (10-27): "since we have our own cloth and physics simulations, make some base shirts, pants, shoes etc, at least like 100 items, to render them in game so I can test the character creator fully."

What

Eleven garments were each a hand-written cut of MakeHuman's body suit. Now every garment is a line in a table (HumanClothes.cpp): a cut (sleeve, neckline, hem, length), a layer offset, a clothFabric() preset and its own colour. 113 garments in seven slots: tops (32), outerwear (20), bottoms (15), skirts (12), dresses (12), socks (6), shoes (11), gloves (5). Adding one is adding a line.

  • garmentPieces(): a dress is a bodice from the suit and a skirt of cloth; a long coat, trench, duster, kimono or cape is a jacket and its tails. garmentShape() stays as the first piece.
  • Gloves are cut from the body's own hands (the body group), the only pieces not from the suit.
  • The fabric drives the cloth simulation (satin drapes, denim and leather hold, a tulle skirt is net) and the fitted pieces' material (garmentMaterial: roughness and gloss).
  • Slots: garmentSlots(), garmentSlotsClash() (a dress takes the top's and the bottom's place). The creator's wear(), random outfits (dress 25%, coat 30%, gloves 15%), the self-test (counts pieces), the Clothes tab (seven sections) and the colour-slot buttons follow. dressUp() logs what it fitted: clothes: 6 piece(s), 2 as cloth: slip dress, trench coat, opera gloves, thigh boots.
  • Share codes are unchanged (a garment travels as its id and colour); the eleven old ids still exist.
  • docs/HUMANS.md "Clothes" rewritten; creator README.

Still plain colours with the fabric's weave and sheen, no printed textures (MakeHuman's .mhclo clothes remain the next step for that).

Tests

  • HumanClothes.EveryGarmentHasItsPart: >= 100 garments, every piece has vertices, a known fabric and slot; necklines and sleeves take cloth away (V < crew, deep V < V, tank < tee < three-quarter < long); dresses are two pieces; slot clashes.
  • HumanClothes.DressedBakeIsOneSkinnedModel: a dress gives two meshes, gloves a skinned one from the skin; satin shinier than wool.
  • Full kke_tests: 1346 passed.
  • check_dependencies, check_game_modules, check_std_includes.
  • Creator self-test under Xvfb, twice: random people in a sweater dress / socks / ankle boots and in a henley, duster, denim skirt, socks, ankle boots, opera gloves: passed.
  • Five fixed outfits under Xvfb in benchmark mode (writes screenshots): slip dress + trench coat + opera gloves + thigh boots; hoodie + jeans + high-tops + fingerless gloves (male); linen shirt + blazer + trousers + loafers (male); ball gown + long gloves + heels (Curvy, turning); camisole + kimono + hakama + sandals (turning). All fitted, no warnings. Screenshots in the handoff folder files/shots/ (I could not view them myself: Kees's look is needed).

🤖 Generated with Claude Code


PR Type

Enhancement


Description

  • Adds 100+ garments via data-driven cuts in HumanClothes.cpp

  • Implements multi-piece garments like dresses and coats

  • Introduces fabric presets for cloth simulation and shading

  • Updates UI to show seven clothing slots including gloves


Diagram Walkthrough

flowchart LR
  Data["Garment Table"] --> Cuts["Cut Definitions"]
  Cuts --> Pieces["Garment Pieces"]
  Pieces --> Cloth["Cloth Simulation"]
  Pieces --> Mesh["Skinned Mesh"]
  Cloth --> Visual["Visual Output"]
  Mesh --> Visual
  UI["Creator UI"] -.-> Select["Select Garment"]
  Select --> Pieces

File Walkthrough

Relevant files
Enhancement
4 files
HumanClothes.cpp
Replaces hardcoded garments with data-driven table of 113 items
+403/-106
CharacterCreatorModule.cpp
Adapts dressing logic for multi-piece garments and gloves
+113/-87
HumanClothes.h
Extends API with garmentPieces, garmentSlots, and material functions
+44/-21 
creator.rml
Adds UI sections for outerwear, dress, and gloves               
+16/-1   
Tests
1 files
test_human.cpp
Adds tests for garment count, pieces, and slot clashes     
+51/-3   
Documentation
2 files
HUMANS.md
Rewrites clothes section to explain new system and fabrics
+39/-13 
README.md
Updates README to mention 100+ garments and fabric presets
+3/-1     

## Title WIP: Character creator: a wardrobe of a hundred garments in the body's own fabrics ___ ### **User description** <!-- claude-projects-attribution: {"github_login":"Khyretos"} --> _Requested by **Kees** · [project thread](https://claude.ai/code/project/chan_01Ds4PKvSLCNCCKr5TJxrV2A?thread=cmsg_01Ds4PKvSLCNCCKr5TJxrV2ABb2WaYyLkc7eUMNFVK1p2Z)_ Stacked on the anime PR #135 (base `claude/creator-anime`), which stacks on the clothes PR #134. One commit. Merge order: #134, #135, then this. **WIP: visual work. Kees's look-OK needed before merging; then drop the WIP prefix.** Kees (10-27): "since we have our own cloth and physics simulations, make some base shirts, pants, shoes etc, at least like 100 items, to render them in game so I can test the character creator fully." ## What Eleven garments were each a hand-written cut of MakeHuman's body suit. Now every garment is a line in a table (`HumanClothes.cpp`): a cut (sleeve, neckline, hem, length), a layer offset, a `clothFabric()` preset and its own colour. **113 garments in seven slots**: tops (32), outerwear (20), bottoms (15), skirts (12), dresses (12), socks (6), shoes (11), gloves (5). Adding one is adding a line. - `garmentPieces()`: a dress is a bodice from the suit and a skirt of cloth; a long coat, trench, duster, kimono or cape is a jacket and its tails. `garmentShape()` stays as the first piece. - Gloves are cut from the body's own hands (the `body` group), the only pieces not from the suit. - The fabric drives the cloth simulation (satin drapes, denim and leather hold, a tulle skirt is net) and the fitted pieces' material (`garmentMaterial`: roughness and gloss). - Slots: `garmentSlots()`, `garmentSlotsClash()` (a dress takes the top's and the bottom's place). The creator's `wear()`, random outfits (dress 25%, coat 30%, gloves 15%), the self-test (counts pieces), the Clothes tab (seven sections) and the colour-slot buttons follow. `dressUp()` logs what it fitted: `clothes: 6 piece(s), 2 as cloth: slip dress, trench coat, opera gloves, thigh boots`. - Share codes are unchanged (a garment travels as its id and colour); the eleven old ids still exist. - docs/HUMANS.md "Clothes" rewritten; creator README. Still plain colours with the fabric's weave and sheen, no printed textures (MakeHuman's `.mhclo` clothes remain the next step for that). ## Tests - [x] `HumanClothes.EveryGarmentHasItsPart`: >= 100 garments, every piece has vertices, a known fabric and slot; necklines and sleeves take cloth away (V < crew, deep V < V, tank < tee < three-quarter < long); dresses are two pieces; slot clashes. - [x] `HumanClothes.DressedBakeIsOneSkinnedModel`: a dress gives two meshes, gloves a skinned one from the skin; satin shinier than wool. - [x] Full `kke_tests`: 1346 passed. - [x] check_dependencies, check_game_modules, check_std_includes. - [x] Creator self-test under Xvfb, twice: random people in a sweater dress / socks / ankle boots and in a henley, duster, denim skirt, socks, ankle boots, opera gloves: passed. - [x] Five fixed outfits under Xvfb in benchmark mode (writes screenshots): slip dress + trench coat + opera gloves + thigh boots; hoodie + jeans + high-tops + fingerless gloves (male); linen shirt + blazer + trousers + loafers (male); ball gown + long gloves + heels (Curvy, turning); camisole + kimono + hakama + sandals (turning). All fitted, no warnings. Screenshots in the handoff folder `files/shots/` (I could not view them myself: Kees's look is needed). 🤖 Generated with [Claude Code](https://claude.com/claude-code) ___ ### **PR Type** Enhancement ___ ### **Description** - Adds 100+ garments via data-driven cuts in HumanClothes.cpp - Implements multi-piece garments like dresses and coats - Introduces fabric presets for cloth simulation and shading - Updates UI to show seven clothing slots including gloves ___ ### Diagram Walkthrough ```mermaid flowchart LR Data["Garment Table"] --> Cuts["Cut Definitions"] Cuts --> Pieces["Garment Pieces"] Pieces --> Cloth["Cloth Simulation"] Pieces --> Mesh["Skinned Mesh"] Cloth --> Visual["Visual Output"] Mesh --> Visual UI["Creator UI"] -.-> Select["Select Garment"] Select --> Pieces ``` <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>4 files</summary><table> <tr> <td><strong>HumanClothes.cpp</strong><dd><code>Replaces hardcoded garments with data-driven table of 113 items</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/engine/src/HumanClothes.cpp">+403/-106</a></td> </tr> <tr> <td><strong>CharacterCreatorModule.cpp</strong><dd><code>Adapts dressing logic for multi-piece garments and gloves</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/engine/src/modules/CharacterCreatorModule.cpp">+113/-87</a></td> </tr> <tr> <td><strong>HumanClothes.h</strong><dd><code>Extends API with garmentPieces, garmentSlots, and material functions</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/engine/include/kke/HumanClothes.h">+44/-21</a>&nbsp; </td> </tr> <tr> <td><strong>creator.rml</strong><dd><code>Adds UI sections for outerwear, dress, and gloves</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/136/head/assets/ui/character_creator/creator.rml">+16/-1</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>1 files</summary><table> <tr> <td><strong>test_human.cpp</strong><dd><code>Adds tests for garment count, pieces, and slot clashes</code>&nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/tests/test_human.cpp">+51/-3</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>HUMANS.md</strong><dd><code>Rewrites clothes section to explain new system and fabrics</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/docs/HUMANS.md">+39/-13</a>&nbsp; </td> </tr> <tr> <td><strong>README.md</strong><dd><code>Updates README to mention 100+ garments and fabric presets</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/games/character_creator/README.md">+3/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
Collaborator

PR Reviewer Guide 🔍

📋 Focus areas: 🔴 3 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

Missing Error Handling for Empty Garment Lists
In dressUp() (line 1270), the code iterates over m_look.clothes and calls findGarment() for each ID. If a garment ID is invalid or missing from the table, findGarment() returns nullptr, but the loop continues without skipping that iteration. This could lead to undefined behavior when accessing info->slot or other fields later in the logic. The code should explicitly check if info is nullptr and skip processing that garment.

Potential Null Pointer Dereference in Random Outfit Generation

In randomise() (line 967-978), the code generates random outfits by iterating over garmentSlots(). For each slot, it collects valid garments into a vector in. However, if in is empty (e.g., no garments defined for a slot), the code proceeds to line 973 where it checks if (in.empty() || u(m_rng) > chance) continue;. While this check prevents dereferencing an empty vector, the logic at line 976 assumes g is non-null. If in is not empty but u(m_rng) selects an index out of bounds (unlikely due to std::uniform_int_distribution), it could cause issues. More importantly, the code does not validate that g is non-null before using it. A safer approach would be to add an explicit null check after selecting g.

for (const std::string& slot : garmentSlots()) {
    if (dress ? (slot == "top" || slot == "bottom") : slot == "dress") continue;
    std::vector<const GarmentInfo*> in;
    for (const GarmentInfo& g : garments())
        if (g.slot == slot) in.push_back(&g);
    const float chance = slot == "socks" ? 0.7f : slot == "outer" ? 0.3f : slot == "gloves" ? 0.15f : 0.95f;
    if (in.empty() || u(m_rng) > chance) continue;
    const GarmentInfo* g = in[std::uniform_int_distribution<size_t>(0, in.size() - 1)(m_rng)];
    // Half the time its own colour (jeans blue, leather black), else any dye.
    const glm::vec3 colour = u(m_rng) < 0.5f ? g->colour : cloth[std::uniform_int_distribution<size_t>(0, cloth.size() - 1)(m_rng)].rgb;
    m_look.clothes.push_back({ g->id, colour });
}
Inconsistent Material Roughness Calculation

In garmentMaterial() (line 453-463), the roughness is calculated as fabric.roughness - 0.3f * fabric.specular, clamped between 0.2f and 0.95f. However, in the old code (before this PR), the roughness was hardcoded based on garment type (e.g., 0.5f for shoes, 0.85f for others). This change introduces a dependency on the clothFabric() preset's properties, which may not be consistent across all fabrics. For example, if a fabric has a very low specular value, the resulting roughness might be too high, leading to unrealistic shading. Consider adding a fallback or validation to ensure the roughness remains within expected ranges for different fabric types.

    m_yaw = 0.0f; // face the camera again
    h.DirtyVariable("turntable");
    if (!args.empty()) setView(args[0].Get<Rml::String>());
});
c.BindEventCallback("hair_style", [this](Rml::DataModelHandle h, Rml::Event&, const Rml::VariantList& args) {
    m_look.hair = args.empty() ? std::string() : args[0].Get<Rml::String>();
    m_hairStyle = m_look.hair;
    h.DirtyVariable("hair_style");
    buildHair();
});
c.BindEventCallback("hair_colour", [this](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList& args) {
## PR Reviewer Guide 🔍 <!-- pr-agent:review:full --> 📋 **Focus areas:** 🔴 3 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> <a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/engine/src/HumanClothes.cpp#L1270-L1276'><strong>Missing Error Handling for Empty Garment Lists</strong></a><br>In `dressUp()` (line 1270), the code iterates over `m_look.clothes` and calls `findGarment()` for each ID. If a garment ID is invalid or missing from the table, `findGarment()` returns `nullptr`, but the loop continues without skipping that iteration. This could lead to undefined behavior when accessing `info->slot` or other fields later in the logic. The code should explicitly check if `info` is `nullptr` and skip processing that garment. <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/engine/src/modules/CharacterCreatorModule.cpp#L967-L978'><strong>Potential Null Pointer Dereference in Random Outfit Generation</strong></a> In `randomise()` (line 967-978), the code generates random outfits by iterating over `garmentSlots()`. For each slot, it collects valid garments into a vector `in`. However, if `in` is empty (e.g., no garments defined for a slot), the code proceeds to line 973 where it checks `if (in.empty() || u(m_rng) > chance) continue;`. While this check prevents dereferencing an empty vector, the logic at line 976 assumes `g` is non-null. If `in` is not empty but `u(m_rng)` selects an index out of bounds (unlikely due to `std::uniform_int_distribution`), it could cause issues. More importantly, the code does not validate that `g` is non-null before using it. A safer approach would be to add an explicit null check after selecting `g`. </summary> ```c++ for (const std::string& slot : garmentSlots()) { if (dress ? (slot == "top" || slot == "bottom") : slot == "dress") continue; std::vector<const GarmentInfo*> in; for (const GarmentInfo& g : garments()) if (g.slot == slot) in.push_back(&g); const float chance = slot == "socks" ? 0.7f : slot == "outer" ? 0.3f : slot == "gloves" ? 0.15f : 0.95f; if (in.empty() || u(m_rng) > chance) continue; const GarmentInfo* g = in[std::uniform_int_distribution<size_t>(0, in.size() - 1)(m_rng)]; // Half the time its own colour (jeans blue, leather black), else any dye. const glm::vec3 colour = u(m_rng) < 0.5f ? g->colour : cloth[std::uniform_int_distribution<size_t>(0, cloth.size() - 1)(m_rng)].rgb; m_look.clothes.push_back({ g->id, colour }); } ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/136/head/engine/src/modules/CharacterCreatorModule.cpp#L453-L463'><strong>Inconsistent Material Roughness Calculation</strong></a> In `garmentMaterial()` (line 453-463), the roughness is calculated as `fabric.roughness - 0.3f * fabric.specular`, clamped between 0.2f and 0.95f. However, in the old code (before this PR), the roughness was hardcoded based on garment type (e.g., 0.5f for shoes, 0.85f for others). This change introduces a dependency on the `clothFabric()` preset's properties, which may not be consistent across all fabrics. For example, if a fabric has a very low specular value, the resulting roughness might be too high, leading to unrealistic shading. Consider adding a fallback or validation to ensure the roughness remains within expected ranges for different fabric types. </summary> ```c++ m_yaw = 0.0f; // face the camera again h.DirtyVariable("turntable"); if (!args.empty()) setView(args[0].Get<Rml::String>()); }); c.BindEventCallback("hair_style", [this](Rml::DataModelHandle h, Rml::Event&, const Rml::VariantList& args) { m_look.hair = args.empty() ? std::string() : args[0].Get<Rml::String>(); m_hairStyle = m_look.hair; h.DirtyVariable("hair_style"); buildHair(); }); c.BindEventCallback("hair_colour", [this](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList& args) { ``` </details> </td></tr> </table> <!-- pr-agent-review-state:v1 {"findings":[{"body":"**Inconsistent Material Roughness Calculation**\n\nIn `garmentMaterial()` (line 453-463), the roughness is calculated as `fabric.roughness - 0.3f * fabric.specular`, clamped between 0.2f and 0.95f. However, in the old code (before this PR), the roughness was hardcoded based on garment type (e.g., 0.5f for shoes, 0.85f for others). This change introduces a dependency on the `clothFabric()` preset's properties, which may not be consistent across all fabrics. For example, if a fabric has a very low specular value, the resulting roughness might be too high, leading to unrealistic shading. Consider adding a fallback or validation to ensure the roughness remains within expected ranges for different fabric types.","finding_id":"181f0c935b35","first_seen":"2026-10-27T17:33:35.713995Z","last_seen":"2026-10-27T17:33:35.713995Z","last_seen_head_sha":"31ac0f726d8b411cfe7a32f918f1ccf243f12a9b","line_end":463,"line_start":453,"path":"engine/src/modules/CharacterCreatorModule.cpp","state":"ACTIVE"},{"body":"**Potential Null Pointer Dereference in Random Outfit Generation**\n\nIn `randomise()` (line 967-978), the code generates random outfits by iterating over `garmentSlots()`. For each slot, it collects valid garments into a vector `in`. However, if `in` is empty (e.g., no garments defined for a slot), the code proceeds to line 973 where it checks `if (in.empty() || u(m_rng) > chance) continue;`. While this check prevents dereferencing an empty vector, the logic at line 976 assumes `g` is non-null. If `in` is not empty but `u(m_rng)` selects an index out of bounds (unlikely due to `std::uniform_int_distribution`), it could cause issues. More importantly, the code does not validate that `g` is non-null before using it. A safer approach would be to add an explicit null check after selecting `g`.","finding_id":"3ff9fa284fe7","first_seen":"2026-10-27T17:33:35.713995Z","last_seen":"2026-10-27T17:33:35.713995Z","last_seen_head_sha":"31ac0f726d8b411cfe7a32f918f1ccf243f12a9b","line_end":978,"line_start":967,"path":"engine/src/modules/CharacterCreatorModule.cpp","state":"ACTIVE"},{"body":"**Missing Error Handling for Empty Garment Lists**\n\nIn `dressUp()` (line 1270), the code iterates over `m_look.clothes` and calls `findGarment()` for each ID. If a garment ID is invalid or missing from the table, `findGarment()` returns `nullptr`, but the loop continues without skipping that iteration. This could lead to undefined behavior when accessing `info->slot` or other fields later in the logic. The code should explicitly check if `info` is `nullptr` and skip processing that garment.","finding_id":"922e4d5ff262","first_seen":"2026-10-27T17:33:35.713995Z","last_seen":"2026-10-27T17:33:35.713995Z","last_seen_head_sha":"31ac0f726d8b411cfe7a32f918f1ccf243f12a9b","line_end":1276,"line_start":1270,"path":"engine/src/HumanClothes.cpp","state":"ACTIVE"}],"last_run":{"complete":true,"excluded_files":[],"head_sha":"31ac0f726d8b411cfe7a32f918f1ccf243f12a9b","kind":"full","run_id":"https://git.kreative-kompas.com/khyretos/kk-engine/commit/31ac0f726d8b411cfe7a32f918f1ccf243f12a9b"},"schema_version":1} -->
Collaborator

PR Code Suggestions ✨

No code suggestions found for the PR.

## PR Code Suggestions ✨ <!-- pr-agent:improve:no-suggestions --> No code suggestions found for the PR.

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

Nothing here blocks the merge (report only).

Check Errors Warnings Files
❌ MARKDOWN · markdownlint 24 0 255
❌ CPP · cppcheck 9 0 953
❌ 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/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/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: "```"]
games/racing/README.md:34 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
games/racing/README.md:76:91 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
games/racing/README.md:77:89 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
…
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/AnimEvents.h
✅ [SUCCESS] engine/include/kke/AnimRig.h
✅ [SUCCESS] engine/include/kke/Animator.h
✅ [SUCCESS] engine/include/kke/AnimeRenderer.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/Bloom.h
✅ [SUCCESS] engine/include/kke/BodyShape.h
✅ [SUCCESS] engine/include/kke/BreakGraph.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-27T17:51:20Z	INFO	[vulndb] Need to update DB
2026-10-27T17:51:20Z	INFO	[vulndb] Downloading vulnerability DB...
2026-10-27T17:51:20Z	INFO	[vulndb] Downloading artifact...	repo="mirror.gcr.io/aquasec/trivy-db:2"
19.69 MiB / 130.01 MiB [--------->__________________________________________________] 15.15% ? p/s ?41.37 MiB / 130.01 MiB [------------------->________________________________________] 31.82% ? p/s ?51.26 MiB / 130.01 MiB [----------------------->____________________________________] 39.43% ? p/s ?59.96 MiB / 130.01 MiB [--------------------->_________________________] 46.12% 66.95 MiB p/s ETA 1s67.88 MiB / 130.01 MiB [------------------------>______________________] 52.21% 66.95 MiB p/s ETA 0s76.06 MiB / 130.01 MiB [--------------------------->___________________] 58.51% 66.95 MiB p/s ETA 0s83.82 MiB / 130.01 MiB [------------------------------>________________] 64.47% 65.20 MiB p/s ETA 0s92.36 MiB / 130.01 MiB [--------------------------------->_____________] 71.04% 65.20 MiB p/s ETA 0s100.75 MiB / 130.01 MiB [----------------------------------->__________] 77.50% 65.20 MiB p/s ETA 0s109.63 MiB / 130.01 MiB [-------------------------------------->_______] 84.33% 63.77 MiB p/s ETA 0s118.03 MiB / 130.01 MiB [----------------------------------------->____] 90.79% 63.77 MiB p/s ETA 0s126.00 MiB / 130.01 MiB [-------------------------------------------->_] 96.92% 63.77 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 61.85 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 61.85 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 61.85 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 57.86 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 57.86 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 57.86 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 54.13 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 54.13 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 54.13 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.63 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.63 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.63 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.37 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.37 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.37 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.31 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-----------------------------------------------] 100.00% 24.03 MiB p/s 5.6s2026-10-27T17:51:26Z	INFO	[vulndb] Artifact successfully downloaded	repo="mirror.gcr.io/aquasec/trivy-db:2"
2026-10-27T17:51:26Z	INFO	[vuln] Vulnerability scanning is enabled
2026-10-27T17:51:26Z	INFO	[misconfig] Misconfiguration scanning is enabled
2026-10-27T17:51:26Z	INFO	[checks-client] Need to update the checks bundle
2026-10-27T17:51:26Z	INFO	[checks-client] Downloading the checks bundle...
234.57 KiB / 234.57 KiB [---------------------------------------------------------] 100.00% ? p/s 0s2026-10-27T17:51:27Z	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-27T17:51:27Z	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-27T17:51:28Z	INFO	Number of language-specific files	num=0
2026-10-27T17:51:28Z	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 (147 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 · ❌ 38 problems in 6 checks · 🧹 3 formatting checks to tidy Nothing here blocks the merge (report only). | | Check | Errors | Warnings | Files | |---|---|---:|---:|---:| | ❌ | MARKDOWN · markdownlint | 24 | 0 | 255 | | ❌ | CPP · cppcheck | 9 | 0 | 953 | | ❌ | 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/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/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: "```"] games/racing/README.md:34 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] games/racing/README.md:76:91 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data] games/racing/README.md:77:89 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data] … ``` </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/AnimEvents.h ✅ [SUCCESS] engine/include/kke/AnimRig.h ✅ [SUCCESS] engine/include/kke/Animator.h ✅ [SUCCESS] engine/include/kke/AnimeRenderer.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/Bloom.h ✅ [SUCCESS] engine/include/kke/BodyShape.h ✅ [SUCCESS] engine/include/kke/BreakGraph.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-27T17:51:20Z INFO [vulndb] Need to update DB 2026-10-27T17:51:20Z INFO [vulndb] Downloading vulnerability DB... 2026-10-27T17:51:20Z INFO [vulndb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-db:2" 19.69 MiB / 130.01 MiB [--------->__________________________________________________] 15.15% ? p/s ?41.37 MiB / 130.01 MiB [------------------->________________________________________] 31.82% ? p/s ?51.26 MiB / 130.01 MiB [----------------------->____________________________________] 39.43% ? p/s ?59.96 MiB / 130.01 MiB [--------------------->_________________________] 46.12% 66.95 MiB p/s ETA 1s67.88 MiB / 130.01 MiB [------------------------>______________________] 52.21% 66.95 MiB p/s ETA 0s76.06 MiB / 130.01 MiB [--------------------------->___________________] 58.51% 66.95 MiB p/s ETA 0s83.82 MiB / 130.01 MiB [------------------------------>________________] 64.47% 65.20 MiB p/s ETA 0s92.36 MiB / 130.01 MiB [--------------------------------->_____________] 71.04% 65.20 MiB p/s ETA 0s100.75 MiB / 130.01 MiB [----------------------------------->__________] 77.50% 65.20 MiB p/s ETA 0s109.63 MiB / 130.01 MiB [-------------------------------------->_______] 84.33% 63.77 MiB p/s ETA 0s118.03 MiB / 130.01 MiB [----------------------------------------->____] 90.79% 63.77 MiB p/s ETA 0s126.00 MiB / 130.01 MiB [-------------------------------------------->_] 96.92% 63.77 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 61.85 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 61.85 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 61.85 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 57.86 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 57.86 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 57.86 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 54.13 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 54.13 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 54.13 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.63 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.63 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.63 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.37 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.37 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.37 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.31 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-----------------------------------------------] 100.00% 24.03 MiB p/s 5.6s2026-10-27T17:51:26Z INFO [vulndb] Artifact successfully downloaded repo="mirror.gcr.io/aquasec/trivy-db:2" 2026-10-27T17:51:26Z INFO [vuln] Vulnerability scanning is enabled 2026-10-27T17:51:26Z INFO [misconfig] Misconfiguration scanning is enabled 2026-10-27T17:51:26Z INFO [checks-client] Need to update the checks bundle 2026-10-27T17:51:26Z INFO [checks-client] Downloading the checks bundle... 234.57 KiB / 234.57 KiB [---------------------------------------------------------] 100.00% ? p/s 0s2026-10-27T17:51:27Z 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-27T17:51:27Z 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-27T17:51:28Z INFO Number of language-specific files num=0 2026-10-27T17:51:28Z 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 (147 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>
All checks were successful
CI / dependencies (pull_request) Successful in 5s
CI / std-includes (pull_request) Successful in 14s
CI / shipping-build (pull_request) Successful in 5m26s
CI / release-build (pull_request) Successful in 7m40s
CI / android (pull_request) Successful in 5m27s
Docs site / site (pull_request) Successful in 11s
lint / megalinter (pull_request) Successful in 59s
Benchmarks / benchmarks (pull_request) Successful in 25m43s
Windows (native) / build (pull_request) Successful in 9m30s
CI / build-test-and-coverage (pull_request) Successful in 1h11m36s
Windows (native) / test (pull_request) Successful in 6m12s
Windows (native) / publish (pull_request) Has been skipped
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/136/head:khyretos/claude/creator-wardrobe
git switch khyretos/claude/creator-wardrobe

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch claude/creator-anime
git merge --no-ff khyretos/claude/creator-wardrobe
git switch khyretos/claude/creator-wardrobe
git rebase claude/creator-anime
git switch claude/creator-anime
git merge --ff-only khyretos/claude/creator-wardrobe
git switch khyretos/claude/creator-wardrobe
git rebase claude/creator-anime
git switch claude/creator-anime
git merge --no-ff khyretos/claude/creator-wardrobe
git switch claude/creator-anime
git merge --squash khyretos/claude/creator-wardrobe
git switch claude/creator-anime
git merge --ff-only khyretos/claude/creator-wardrobe
git switch claude/creator-anime
git merge khyretos/claude/creator-wardrobe
git push origin claude/creator-anime
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!136
No description provided.