import json from missionledger.dsl import LocalProblem from missionledger.planner import ShadowPlanner from missionledger.dsl import PlanDelta from missionledger.safety import verify_plan from missionledger.governance import GovernanceLedger def test_shadow_planner_and_safety_verification(): # Basic LocalProblem with a modest risk budget lp = LocalProblem( asset_objectives={"rover-1": {"energy": 1.0, "time": 2.0}}, variables={"start_time": 0}, constraints=["no_overload"], risk_budget=1.0, ) primary_delta = PlanDelta(delta={"basic_plan": True}, version=1, metadata={}) planner = ShadowPlanner(lp) shadow = planner.plan(primary_delta) # Validate safety proof via the lightweight verifier assert shadow is not None assert isinstance(shadow.plan_delta, PlanDelta) assert shadow.proof_valid in (True, False) # Sanity check: the safety verifier should be deterministic for the MVP safe = verify_plan(lp, shadow.plan_delta) assert safe == shadow.proof_valid # Governance ledger usage: log an entry and inspect its wiring gl = GovernanceLedger(device_id="test-device") gl.log({"type": "plan_evaluation", "delta_version": shadow.plan_delta.version}) assert len(gl.entries) == 1 assert "parent" in gl.entries[0] # latest_hash should be a non-empty string hash assert isinstance(gl.latest_hash(), str) assert len(gl.latest_hash()) > 0