From cc39590947315d772c1732b690e2a9675196a2bb Mon Sep 17 00:00:00 2001 From: agent-14fd4b738639d573 Date: Fri, 24 Apr 2026 20:51:31 +0200 Subject: [PATCH] build(agent): melter#14fd4b iteration --- gravityweave/__init__.py | 4 +++ gravityweave/safety_stub.py | 59 ++++++++++++++++++++++++++++++++++ gravityweave/witness.py | 64 +++++++++++++++++++++++++++++++++++++ tests/test_witness_stub.py | 31 ++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 gravityweave/safety_stub.py create mode 100644 gravityweave/witness.py create mode 100644 tests/test_witness_stub.py diff --git a/gravityweave/__init__.py b/gravityweave/__init__.py index 0417b04..81d2ccc 100644 --- a/gravityweave/__init__.py +++ b/gravityweave/__init__.py @@ -12,6 +12,8 @@ from .ledger import GovernanceLedger from .adapters import sat_planner, relay_module from .solver import ADMMNode from .delta_synopsis import build_synopsis, build_priority_key, serialize_synopsis, deserialize_synopsis, verify_synopsis +from .witness import generate_feasibility_witness +from .safety_stub import extract_safety_stub from .mission import MissionTask, ContactWindow, MissionNode, MissionSimulator __all__ = [ @@ -28,6 +30,8 @@ __all__ = [ "serialize_synopsis", "deserialize_synopsis", "verify_synopsis", + "generate_feasibility_witness", + "extract_safety_stub", "MissionTask", "ContactWindow", "MissionNode", diff --git a/gravityweave/safety_stub.py b/gravityweave/safety_stub.py new file mode 100644 index 0000000..dc6564f --- /dev/null +++ b/gravityweave/safety_stub.py @@ -0,0 +1,59 @@ +"""Safety stub extractor. + +Extracts a tiny, conservative safety stub from a PlanDelta. The stub contains +only the minimal actions that are safety-related (e.g., safety_critical +commitments) and a provenance hash linking back to the full delta snapshot. + +The stub is intentionally tiny and can be executed in constrained runtimes. +""" +from typing import Dict, Any, List +from .plan_delta import PlanDelta +import json +import hashlib + + +def extract_safety_stub(delta: PlanDelta) -> Dict[str, Any]: + """Return a compact stub containing safety-critical commitments and a hash. + + The stub fields: + - stub_commitments: list of commitments filtered for safety_critical or + those that match a small whitelist (e.g., type == 'safe-hold') + - provenance: short hash of the full delta snapshot + - origin: delta.origin for traceability + """ + stub_commitments: List[Dict[str, Any]] = [] + + for c in delta.commitments.value(): + if not isinstance(c, dict): + continue + # Keep explicit safety-critical commitments + if c.get("safety_critical"): + stub_commitments.append(_minimalize_commitment(c)) + continue + # Keep very small whitelist of actions that are safe fallbacks + if c.get("type") in ("safe-hold", "thruster-off", "throttle-back"): + stub_commitments.append(_minimalize_commitment(c)) + + prov = _short_hash(delta.snapshot()) + + return { + "stub_commitments": stub_commitments, + "provenance": prov, + "origin": getattr(delta, "origin", ""), + } + + +def _minimalize_commitment(c: Dict[str, Any]) -> Dict[str, Any]: + """Return a tiny projection of a commitment preserving key invariants. + + Keep only fields that are likely necessary to reason about safety. + """ + keys = ["task_id", "type", "fuel", "power", "deadline_step", "safety_critical"] + return {k: c[k] for k in keys if k in c} + + +def _short_hash(obj: Any) -> str: + import hashlib + + raw = json.dumps(obj, sort_keys=True, separators=(",", ":")) + return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16] diff --git a/gravityweave/witness.py b/gravityweave/witness.py new file mode 100644 index 0000000..456ade0 --- /dev/null +++ b/gravityweave/witness.py @@ -0,0 +1,64 @@ +"""Feasibility witness generator. + +Produces a compact, quickly-verifiable witness for a PlanDelta describing +coarse resource bounds (fuel, power, payload size). The witness is intended +to be inexpensive to compute and small to attach to custody decisions. +""" +from typing import Dict, Any +from .plan_delta import PlanDelta +import json + + +def generate_feasibility_witness(delta: PlanDelta) -> Dict[str, Any]: + """Produce a compact witness with conservative bounds. + + The witness includes: + - total_fuel: sum of declared fuel in commitments + - total_power: sum of declared power in commitments + - max_payload_bytes: sum of payload_bytes fields (upper bound) + - commitment_count: number of commitments + - summary_hash: short hash of the snapshot for quick correlation + + These values are intentionally coarse (conservative sums) so a lightweight + validator can quickly spot obvious infeasibility (e.g., fuel required > node fuel). + """ + total_fuel = 0.0 + total_power = 0.0 + max_payload = 0 + count = 0 + + for c in delta.commitments.value(): + if not isinstance(c, dict): + continue + count += 1 + try: + total_fuel += float(c.get("fuel", 0.0)) + except Exception: + total_fuel += 0.0 + try: + total_power += float(c.get("power", 0.0)) + except Exception: + total_power += 0.0 + try: + max_payload += int(c.get("payload_bytes", 0)) + except Exception: + max_payload += 0 + + snapshot = delta.snapshot() + summary_hash = _short_hash(snapshot) + + return { + "total_fuel": total_fuel, + "total_power": total_power, + "max_payload_bytes": max_payload, + "commitment_count": count, + "summary_hash": summary_hash, + } + + +def _short_hash(obj: Any) -> str: + """Return a short hex digest for the JSON of obj.""" + import hashlib + + raw = json.dumps(obj, sort_keys=True, separators=(",", ":")) + return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16] diff --git a/tests/test_witness_stub.py b/tests/test_witness_stub.py new file mode 100644 index 0000000..002f728 --- /dev/null +++ b/tests/test_witness_stub.py @@ -0,0 +1,31 @@ +from gravityweave.plan_delta import PlanDelta +from gravityweave.witness import generate_feasibility_witness +from gravityweave.safety_stub import extract_safety_stub + + +def test_generate_feasibility_witness_basic(): + pd = PlanDelta(origin="node1") + pd.commitments.add({"type": "burn", "fuel": 5.0, "power": 1.0, "payload_bytes": 100}) + pd.commitments.add({"type": "downlink", "fuel": 0.0, "power": 2.0, "payload_bytes": 200}) + + w = generate_feasibility_witness(pd) + assert w["total_fuel"] == 5.0 + assert w["total_power"] == 3.0 + assert w["max_payload_bytes"] == 300 + assert w["commitment_count"] == 2 + assert isinstance(w["summary_hash"], str) and len(w["summary_hash"]) == 16 + + +def test_extract_safety_stub_filters(): + pd = PlanDelta(origin="satA") + pd.commitments.add({"task_id": "t1", "type": "science", "safety_critical": True, "fuel": 1.0}) + pd.commitments.add({"task_id": "t2", "type": "downlink", "fuel": 0.0}) + pd.commitments.add({"task_id": "t3", "type": "safe-hold", "fuel": 0.0}) + + stub = extract_safety_stub(pd) + # should include t1 (safety_critical) and t3 (whitelist) + ids = {c.get("task_id") for c in stub["stub_commitments"]} + assert "t1" in ids + assert "t3" in ids + assert stub["origin"] == "satA" + assert isinstance(stub["provenance"], str) and len(stub["provenance"]) == 16