Sprint 28 client deliverables — character visual pipeline foundation:
#702: Port spike shaders (toon, toon_masked, outline), skin tone textures (9 PNGs), and GLTF import setting to production client
#703: CharacterVisualDescriptor GDScript class — 11-variant BodyType enum, wire format encode/decode, body_type_key mapping for asset path resolution
#704: CharacterVisual compositor — runtime 3D character assembler with slot architecture (body segments, head, hair, facial hair, eyebrows, clothing, accessories), BoneAttachment3D, clothing coverage via coverage.json, recolor mask loading, skin tone tinting, bone validation
Port proven shaders (toon, toon_masked, outline) and skin tone textures
from quaternius-aesthetic spike to production client. Import 65-bone
armature and Universal Animation Libraries (UAL1 + UAL2). Set
gltf/embedded_image_handling=3 in project.godot (critical import setting).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CharacterVisualDescriptor (11-variant BodyType enum, wire format
encode/decode, body_type_key mapping) and CharacterVisual compositor
(runtime 3D character assembler with slot architecture, BoneAttachment3D,
clothing coverage, recolor mask loading, skin tone tinting, bone
validation). Includes 64 unit tests and Blender utility scripts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Well-structured compositor with thorough test-first coverage. Skip-with-warning pattern is right for this stage.
#
File
Severity
Issue
1
character_visual.gd:225
warning
_clear() calls _rebuild_outlines() before mesh lists are cleared — rebuilds against about-to-be-freed meshes
2
character_visual.gd:509
warning
_load_accessories() uses _bone_attachments.back() — fragile implicit coupling to _attach_to_bone() internals
3
character_visual.gd:57-66
warning
SLOT_TO_BONE missing 6 of 12 accessory slots from architecture doc
4
toon_masked.gdshader:2
warning
cull_disabled without ALPHA output — renders interior faces of all toon_masked geometry
5
toon_masked.gdshader:31
warning
tint_color alpha silently discarded (inconsistent with toon.gdshader which does write ALPHA)
6
character_visual_descriptor.gd:132
suggestion
skin_tone from from_dict() not range-validated (deferred to use-time clamp)
7
blender_list_bones.py
suggestion
No docstring, no -- argv guard (inconsistent with other Blender scripts)
8
test_character_visual_sprint28.gd:162
suggestion
Reload test sentinel <= count * 2 is too loose — should be == count
Tyre (Architecture): REQUEST_CHANGES
Compositor matches the approved asset organization proposal well. Path derivation is purely convention-based, descriptor contract is well-typed. Two contract issues need fixing.
#
File
Severity
Issue
1
character_visual.gd:57-66
warning
SLOT_TO_BONE missing 4 slots from spec (earring_l/r, necklace, wrist_l/r); earring vs earring_l/r naming mismatch creates silent descriptor drops
2
decisions/architecture.md D-160
warning
D-160 says "15 base segments" but implementation and org doc both use 17; test comment at line 19 propagates stale figure
3
character_visual.gd:553
suggestion
Shared outline ShaderMaterial will need per-instance duplication when D-152 LOD tiers arrive
4
character_visual.gd:225
suggestion
_clear() call ordering for outline teardown is non-obvious — needs dependency comment
5
blender_list_bones.py
suggestion
No docstring, no __main__ guard, crashes without -- separator
Verdict: CHANGES REQUESTED
Overlap: Both reviewers flag the SLOT_TO_BONE gap (Hoshe #3 / Tyre #1) and _clear() ordering (Hoshe #1 / Tyre #4). Unique warnings: Hoshe catches the _bone_attachments.back() fragility and the shader cull_disabled/ALPHA issues; Tyre catches the D-160 stale segment count.
All 8 fixes from round 1 verified. Three suggestions for follow-up:
#
File
Severity
Issue
1
toon_masked.gdshader
suggestion
No depth_draw_alpha_prepass — cutout alpha on hair/accessories may sort incorrectly
2
character_visual.gd:229
suggestion
_accessory_attachments / _bone_attachments coupling is implicit — add comment
3
test_character_visual_sprint28.gd:143
suggestion
Reload test checks child count, not internal array sizes — gap in regression coverage
Tyre (Architecture): APPROVE
All 5 fixes from round 1 verified. One counting comment inconsistency and doc drift:
#
File
Severity
Issue
1
character_visual.gd:37
warning
Comment says "17 base" but array has 18 entries (torso_upper counted in array). Suggest: "18 segments (16 body + eyes + eyebrows; torso_upper hidden by default)"
2
test_character_visual_sprint28.gd:19
suggestion
Test docstring says "17 bone-group regions" — same counting confusion
3
character_visual.gd:583
suggestion
_rebuild_outlines() needs comment "must be called after segment visibility is finalized"
4
character_visual_descriptor.gd:5
suggestion
No mechanism to detect wire format drift from server
5
character-asset-organization.md Section 3.3
suggestion
Accessory slot list (9 entries) doesn't match code (12 entries with earring l/r, wrist l/r)
Verdict: APPROVED
Both reviewers approve. Segment count comment is the only warning-level item — it's a comment issue, not a code bug (the array itself is correct). Doc drift on accessory slots should be fixed as a follow-up.
Re-reviewed by Hoshe (code quality) and Tyre (architecture) via /pr-review
## Review: client -> main (type: code) — Round 2
### Hoshe (Code Quality): APPROVE
All 8 fixes from round 1 verified. Three suggestions for follow-up:
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `toon_masked.gdshader` | suggestion | No `depth_draw_alpha_prepass` — cutout alpha on hair/accessories may sort incorrectly |
| 2 | `character_visual.gd:229` | suggestion | `_accessory_attachments` / `_bone_attachments` coupling is implicit — add comment |
| 3 | `test_character_visual_sprint28.gd:143` | suggestion | Reload test checks child count, not internal array sizes — gap in regression coverage |
### Tyre (Architecture): APPROVE
All 5 fixes from round 1 verified. One counting comment inconsistency and doc drift:
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `character_visual.gd:37` | warning | Comment says "17 base" but array has 18 entries (torso_upper counted in array). Suggest: "18 segments (16 body + eyes + eyebrows; torso_upper hidden by default)" |
| 2 | `test_character_visual_sprint28.gd:19` | suggestion | Test docstring says "17 bone-group regions" — same counting confusion |
| 3 | `character_visual.gd:583` | suggestion | `_rebuild_outlines()` needs comment "must be called after segment visibility is finalized" |
| 4 | `character_visual_descriptor.gd:5` | suggestion | No mechanism to detect wire format drift from server |
| 5 | `character-asset-organization.md` Section 3.3 | suggestion | Accessory slot list (9 entries) doesn't match code (12 entries with earring l/r, wrist l/r) |
### Verdict: APPROVED
Both reviewers approve. Segment count comment is the only warning-level item — it's a comment issue, not a code bug (the array itself is correct). Doc drift on accessory slots should be fixed as a follow-up.
---
*Re-reviewed by Hoshe (code quality) and Tyre (architecture) via `/pr-review`*
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Sprint 28 client deliverables — character visual pipeline foundation:
64 unit tests covering descriptor round-trips and compositor API.
Test plan
client/assets/characters/gltf/embedded_image_handling=3inclient/project.godotcharacter_visual.gdandcharacter_visual_descriptor.gd🤖 Generated with Claude Code
Review: client -> main (type: code)
Hoshe (Code Quality): REQUEST_CHANGES
Well-structured compositor with thorough test-first coverage. Skip-with-warning pattern is right for this stage.
character_visual.gd:225_clear()calls_rebuild_outlines()before mesh lists are cleared — rebuilds against about-to-be-freed meshescharacter_visual.gd:509_load_accessories()uses_bone_attachments.back()— fragile implicit coupling to_attach_to_bone()internalscharacter_visual.gd:57-66SLOT_TO_BONEmissing 6 of 12 accessory slots from architecture doctoon_masked.gdshader:2cull_disabledwithoutALPHAoutput — renders interior faces of all toon_masked geometrytoon_masked.gdshader:31tint_coloralpha silently discarded (inconsistent withtoon.gdshaderwhich does writeALPHA)character_visual_descriptor.gd:132skin_tonefromfrom_dict()not range-validated (deferred to use-time clamp)blender_list_bones.py--argv guard (inconsistent with other Blender scripts)test_character_visual_sprint28.gd:162<= count * 2is too loose — should be== countTyre (Architecture): REQUEST_CHANGES
Compositor matches the approved asset organization proposal well. Path derivation is purely convention-based, descriptor contract is well-typed. Two contract issues need fixing.
character_visual.gd:57-66SLOT_TO_BONEmissing 4 slots from spec (earring_l/r,necklace,wrist_l/r);earringvsearring_l/rnaming mismatch creates silent descriptor dropsdecisions/architecture.mdD-160character_visual.gd:553ShaderMaterialwill need per-instance duplication when D-152 LOD tiers arrivecharacter_visual.gd:225_clear()call ordering for outline teardown is non-obvious — needs dependency commentblender_list_bones.py__main__guard, crashes without--separatorVerdict: CHANGES REQUESTED
Overlap: Both reviewers flag the
SLOT_TO_BONEgap (Hoshe #3 / Tyre #1) and_clear()ordering (Hoshe #1 / Tyre #4). Unique warnings: Hoshe catches the_bone_attachments.back()fragility and the shadercull_disabled/ALPHAissues; Tyre catches the D-160 stale segment count.Priority fixes:
SLOT_TO_BONE— sync with architecture doc (12 slots, L/R earring split)_clear()— fix ordering or extract teardown-only methodtoon_masked.gdshader— either removecull_disabledor justify with comment + addALPHAoutput for consistency_attach_to_bone()— return theBoneAttachment3Dnode instead ofboolReviewed by Hoshe (code quality) and Tyre (architecture) via
/pr-reviewReview: client -> main (type: code) — Round 2
Hoshe (Code Quality): APPROVE
All 8 fixes from round 1 verified. Three suggestions for follow-up:
toon_masked.gdshaderdepth_draw_alpha_prepass— cutout alpha on hair/accessories may sort incorrectlycharacter_visual.gd:229_accessory_attachments/_bone_attachmentscoupling is implicit — add commenttest_character_visual_sprint28.gd:143Tyre (Architecture): APPROVE
All 5 fixes from round 1 verified. One counting comment inconsistency and doc drift:
character_visual.gd:37test_character_visual_sprint28.gd:19character_visual.gd:583_rebuild_outlines()needs comment "must be called after segment visibility is finalized"character_visual_descriptor.gd:5character-asset-organization.mdSection 3.3Verdict: APPROVED
Both reviewers approve. Segment count comment is the only warning-level item — it's a comment issue, not a code bug (the array itself is correct). Doc drift on accessory slots should be fixed as a follow-up.
Re-reviewed by Hoshe (code quality) and Tyre (architecture) via
/pr-reviewPull request closed