From 06e8290bfccb9d60bf8b0d62e0074accb762cf7f Mon Sep 17 00:00:00 2001 From: Gonzalo Vidal <35148159+Gonza10V@users.noreply.github.com> Date: Wed, 6 May 2026 00:19:52 -0600 Subject: [PATCH] Fallback to arbitrary lvl2 order when region_order cannot be satisfied --- src/buildcompiler/stages/assembly_lvl2.py | 26 ++++++++++++++++++----- tests/unit/stages/test_assembly_lvl2.py | 17 +++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/buildcompiler/stages/assembly_lvl2.py b/src/buildcompiler/stages/assembly_lvl2.py index 44d6b81..31d6eb6 100644 --- a/src/buildcompiler/stages/assembly_lvl2.py +++ b/src/buildcompiler/stages/assembly_lvl2.py @@ -74,6 +74,22 @@ def run( region_identities=region_identities, constraints=constraints, ) + warning_logs: list[str] = [] + if route_selection.selected is None and constraints.get("region_order"): + relaxed_constraints = { + key: value for key, value in constraints.items() if key != "region_order" + } + relaxed_selection = self.selector.select_lvl2_route( + request_id=request.id, + region_identities=region_identities, + constraints=relaxed_constraints, + ) + if relaxed_selection.selected is not None: + route_selection = relaxed_selection + warning_logs.append( + "Unable to satisfy region_order constraint; proceeding with an arbitrary compatible order." + ) + route = route_selection.selected artifacts = self._route_artifacts(route, route_selection.rejected) if route is None: @@ -85,7 +101,8 @@ def run( protocol_artifacts=artifacts, logs=[ "No lvl2 route selected by CompatibilitySelector. Provide explicit region_order " - "or enable large-order search for large designs." + "or enable large-order search for large designs.", + *warning_logs, ], ) @@ -157,7 +174,8 @@ def run( missing_inputs=missing_inputs, protocol_artifacts=artifacts, logs=[ - f"Blocked lvl2 assembly for {request.id}; missing {len(missing_inputs)} required input(s)." + *warning_logs, + f"Blocked lvl2 assembly for {request.id}; missing {len(missing_inputs)} required input(s).", ], ) @@ -208,6 +226,7 @@ def run( json_intermediate=json_intermediate, protocol_artifacts=artifacts, logs=[ + *warning_logs, f"Selected lvl2 route with {len(route.selected_lvl1_plasmids)} lvl1 plasmid(s).", *assembly_result.logs, ], @@ -216,9 +235,6 @@ def run( def _extract_region_identities( self, module_definition: sbol2.ModuleDefinition, constraints: Mapping[str, Any] ) -> list[str]: - constrained = constraints.get("region_order") - if constrained: - return list(constrained) for key in ("engineered_region_identities", "region_identities"): values = constraints.get(key) if values: diff --git a/tests/unit/stages/test_assembly_lvl2.py b/tests/unit/stages/test_assembly_lvl2.py index 281d317..17466c7 100644 --- a/tests/unit/stages/test_assembly_lvl2.py +++ b/tests/unit/stages/test_assembly_lvl2.py @@ -189,3 +189,20 @@ def test_assembly_lvl2_region_order_constraint_is_hard(): assert result.status == StageStatus.SUCCESS assert result.protocol_artifacts["selected_route"]["region_order"] == order + + +def test_assembly_lvl2_incomplete_region_order_falls_back_with_warning(): + doc, module, regions = _module_doc() + inv = _inventory(regions) + stage = AssemblyLvl2Stage(inventory=inv, assembly_service=_FakeAssemblyService([])) + + incomplete_order = [regions[0]] + result = stage.run( + _request(module.identity, constraints={"region_order": incomplete_order}), + source_document=doc, + target_document=sbol2.Document(), + ) + + assert result.status == StageStatus.SUCCESS + assert result.protocol_artifacts["selected_route"] is not None + assert any("Unable to satisfy region_order constraint" in log for log in result.logs)