From 439dc171c5d2456a6deefdf98dd7161217f0b33a Mon Sep 17 00:00:00 2001 From: Christian Heinemann Date: Tue, 14 Jul 2026 18:52:33 +0200 Subject: [PATCH] Keep void-node constructor in homogeneous gene during validation In a homogeneous gene the effective cell type comes from the first node, so a void node is expressed as that type and legitimately keeps its constructor (matching ConstructorProcessor/MutationProcessor). The genome editor's validateAndCorrect stripped it unconditionally, making such genes appear unreachable in the editor. Co-Authored-By: Claude Opus 4.8 --- .../EngineInterface/DescValidationService.cpp | 6 ++++-- .../DescValidationServiceTests.cpp | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/source/EngineInterface/DescValidationService.cpp b/source/EngineInterface/DescValidationService.cpp index 54e682f46..3a72c4835 100644 --- a/source/EngineInterface/DescValidationService.cpp +++ b/source/EngineInterface/DescValidationService.cpp @@ -284,8 +284,10 @@ void DescValidationService::validateAndCorrect(GenomeDesc& genome) } } - // Validate optional constructor field (void cells cannot have a constructor) - if (nodeType == CellType_Void) { + // Validate optional constructor field. A void node normally cannot have a constructor, but in a homogeneous gene the + // effective cell type is taken from the first node, so a void node is actually expressed as that type and keeps its + // constructor (see MutationProcessor::applyMutations and ConstructorProcessor::createConstructionData). + if (nodeType == CellType_Void && !gene._homogeneousCellType) { node._constructor = std::nullopt; } else if (node._constructor.has_value()) { auto& constructor = node._constructor.value(); diff --git a/source/EngineInterfaceTests/DescValidationServiceTests.cpp b/source/EngineInterfaceTests/DescValidationServiceTests.cpp index 9ec3c8a59..8f66ce69f 100644 --- a/source/EngineInterfaceTests/DescValidationServiceTests.cpp +++ b/source/EngineInterfaceTests/DescValidationServiceTests.cpp @@ -29,6 +29,23 @@ TEST_F(DescValidationServiceTests, genome_removesConstructorFromVoidCell) EXPECT_FALSE(genome._genes.at(0)._nodes.at(1)._constructor.has_value()); } +TEST_F(DescValidationServiceTests, genome_keepsConstructorOnVoidCellInHomogeneousGene) +{ + // In a homogeneous gene the effective cell type comes from the first node, so a void node is expressed as that type + // and legitimately keeps its constructor (matching ConstructorProcessor and MutationProcessor). + auto genome = GenomeDesc().genes({ + GeneDesc().homogeneousCellType(true).nodes({ + NodeDesc(), + NodeDesc().cellType(VoidGenomeDesc()).constructor(ConstructorGenomeDesc().geneIndex(0).separation(true)), + NodeDesc(), + }), + }); + + _descValidationService.validateAndCorrect(genome); + + EXPECT_TRUE(genome._genes.at(0)._nodes.at(1)._constructor.has_value()); +} + TEST_F(DescValidationServiceTests, genome_keepsConstructorOnNonVoidCell) { auto genome = GenomeDesc().genes({