diff --git a/nova_plan/catopt_bridge.py b/nova_plan/catopt_bridge.py index dad46d9..25dc6d1 100644 --- a/nova_plan/catopt_bridge.py +++ b/nova_plan/catopt_bridge.py @@ -73,7 +73,19 @@ def protocol_example(local: LocalProblem) -> str: return f"CatOpt Protocol Example: agent={obj.to_dict().get('agent_id')}" -__all__ = ["Object", "Morphism", "Functor", "protocol_example"] +__all__ = ["Object", "Morphism", "Functor", "protocol_example", "validate_contracts"] + + +def validate_contracts(obj: Object, morphism: Morphism) -> bool: + """Validate basic contract compatibility between Object and Morphism. + + Ensures that the delta keys carried by the Morphism are a subset of the + Object's local variables. This is a lightweight guard to catch obvious + contract mismatches early in the MVP bridge. + """ + obj_vars = set(obj.to_dict().get("variables", {}).keys()) + delta_keys = set(morphism.delta.keys()) + return delta_keys.issubset(obj_vars) def to_object(local: LocalProblem) -> Object: diff --git a/tests/test_catopt_bridge.py b/tests/test_catopt_bridge.py index 036a456..745bfa2 100644 --- a/tests/test_catopt_bridge.py +++ b/tests/test_catopt_bridge.py @@ -2,7 +2,7 @@ import json import pytest from nova_plan.dsl import LocalProblemDSL -from nova_plan.catopt_bridge import Object, Morphism, to_object, to_morphism, bridge_example +from nova_plan.catopt_bridge import Object, Morphism, to_object, to_morphism, bridge_example, validate_contracts def test_local_problem_to_object_and_morphism_basic(): @@ -47,3 +47,20 @@ def test_bridge_example_integration(): obj_payload = res["object"] assert obj_payload["agent_id"] == "habitat1" assert obj_payload["variables"] == {"a": 2.0} + + +def test_validate_contracts_basic_and_negative(): + dsl = LocalProblemDSL( + agent_id="rover1", + objective_expr='vars["x"]', + variables={"x": 1.0}, + constraints={}, + ) + lp = dsl.to_local_problem() + obj = Object(lp) + morph = to_morphism(delta={"x": 0.5}, source="rover1", target="habitat") + assert validate_contracts(obj, morph) is True + + # Mismatched delta key should fail + bad_morph = Morphism(delta={"y": 0.2}, source="rover1", target="habitat") + assert validate_contracts(obj, bad_morph) is False