From 8e73b0ac327d4771bded865856a4a9bf79ae9ea6 Mon Sep 17 00:00:00 2001 From: agent-856f80a92b1141b4 Date: Fri, 24 Apr 2026 21:04:28 +0200 Subject: [PATCH] build(agent): weasel-1#856f80 iteration --- .../ir.py | 88 +++++++++++++++++++ tests/test_ir_and_crdt.py | 37 ++++++++ 2 files changed, 125 insertions(+) create mode 100644 interplanetary_edge_orchestrator_privacy/ir.py create mode 100644 tests/test_ir_and_crdt.py diff --git a/interplanetary_edge_orchestrator_privacy/ir.py b/interplanetary_edge_orchestrator_privacy/ir.py new file mode 100644 index 0000000..98fb0be --- /dev/null +++ b/interplanetary_edge_orchestrator_privacy/ir.py @@ -0,0 +1,88 @@ +"""Canonical IR utilities and lightweight CRDT-like reconciliation. + +This module provides: +- simple PlanDelta JSON (de)serialization helpers +- a deterministic, op-based merge function for PlanDelta lists that + applies deltas in timestamp/author order to produce a deterministic + reconciliation result suitable for offline-first replay. + +The implementations are intentionally minimal and dependency-free so +they remain easy to test and extend. +""" +from __future__ import annotations + +import json +from typing import List, Dict, Any + + +def plan_delta_to_json(plan_delta: dict) -> str: + """Serialize a PlanDelta-like dict to a compact JSON string. + + Expects a mapping with keys: delta (dict), timestamp (float), author (str), + contract_id (str), signature (str). Missing fields are tolerated. + """ + # Ensure a stable ordering for determinism + return json.dumps(plan_delta, sort_keys=True, separators=(",", ":")) + + +def plan_delta_from_json(s: str) -> dict: + """Deserialize a PlanDelta JSON string to a dict. + + This is a thin wrapper around json.loads but keeps the IR surface + explicit and easy to extend with schema checks later. + """ + return json.loads(s) + + +def _sort_key_for_pd(pd: dict) -> Any: + # Some PlanDeltas may have NaN timestamps; use 0 for missing/NaN to keep + # sort stable. Then use author to break ties deterministically. + ts = pd.get("timestamp") + try: + if ts is None or (isinstance(ts, float) and (ts != ts)): + ts_val = 0.0 + else: + ts_val = float(ts) + except Exception: + ts_val = 0.0 + author = pd.get("author") or "" + return (ts_val, author) + + +def merge_plan_deltas(deltas: List[dict]) -> dict: + """Deterministically merge a list of PlanDelta-like dicts. + + Behavior: + - Sort deltas by (timestamp ascending, author ascending) to obtain a + deterministic application order. + - Apply each delta's "delta" mapping sequentially; later writes override + earlier ones for the same keys. + - Return a new PlanDelta-like dict with merged delta and provenance + fields taken from the last-applied delta where available. + + This is intentionally simple: it's an op-based reconciliation suitable as + a starting point for CRDT-style deterministic replay across reconnects. + """ + if not deltas: + return {"delta": {}, "timestamp": None, "author": "", "contract_id": "", "signature": ""} + + # Work on shallow copies and sort for deterministic application order + ordered = sorted([dict(d) for d in deltas], key=_sort_key_for_pd) + merged: Dict[str, Any] = {} + last_prov = {} + for pd in ordered: + inner = pd.get("delta") or {} + # Apply key set from this delta + for k, v in inner.items(): + merged[k] = v + # record provenance (take the provenance of the last applied delta) + last_prov = {k: pd.get(k) for k in ("timestamp", "author", "contract_id", "signature")} + + result = { + "delta": merged, + "timestamp": last_prov.get("timestamp"), + "author": last_prov.get("author") or "", + "contract_id": last_prov.get("contract_id") or "", + "signature": last_prov.get("signature") or "", + } + return result diff --git a/tests/test_ir_and_crdt.py b/tests/test_ir_and_crdt.py new file mode 100644 index 0000000..4d81fee --- /dev/null +++ b/tests/test_ir_and_crdt.py @@ -0,0 +1,37 @@ +import random +from interplanetary_edge_orchestrator_privacy.ir import ( + plan_delta_to_json, + plan_delta_from_json, + merge_plan_deltas, +) + + +def make_pd(delta: dict, timestamp: float, author: str): + return {"delta": delta, "timestamp": timestamp, "author": author, "contract_id": "c1", "signature": f"sig-{author}"} + + +def test_plan_delta_json_roundtrip(): + pd = make_pd({"x": 1, "y": 2}, timestamp=123.45, author="agent-A") + s = plan_delta_to_json(pd) + pd2 = plan_delta_from_json(s) + assert pd2["delta"]["x"] == 1 + assert pd2["author"] == "agent-A" + + +def test_merge_plan_deltas_is_deterministic(): + # Create three deltas with overlapping keys and different timestamps + p1 = make_pd({"a": 1}, timestamp=1.0, author="A") + p2 = make_pd({"b": 2}, timestamp=2.0, author="B") + p3 = make_pd({"a": 3}, timestamp=3.0, author="C") + + baseline = merge_plan_deltas([p1, p2, p3]) + # expected: a overwritten by p3, b from p2 + assert baseline["delta"]["a"] == 3 + assert baseline["delta"]["b"] == 2 + + # Shuffle inputs many times and assert merge result is identical + for _ in range(10): + arr = [p1, p2, p3][:] + random.shuffle(arr) + m = merge_plan_deltas(arr) + assert m == baseline