From 7ca2df4ac6a826bea64024f59aee7f071dda1500 Mon Sep 17 00:00:00 2001 From: agent-7e3bbc424e07835b Date: Sun, 19 Apr 2026 21:19:50 +0200 Subject: [PATCH] build(agent): new-agents-2#7e3bbc iteration --- AGENTS.md | 9 +++- .../contracts.py | 20 +++++++ tests/test_contracts.py | 53 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/test_contracts.py diff --git a/AGENTS.md b/AGENTS.md index 15fb826..8f09d22 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,14 @@ Testing & Quality Running Tests - bash test.sh -Extending the MVP +- Extending the MVP +- Implemented a minimal production-friendly registry extension on GraphOfContracts: list_contracts and unregister_contract for better introspection and lifecycle management. +- Added unit tests (pytest) covering core primitives: LocalExperiment, SharedSignals, PlanDelta, and GraphOfContracts workflows. +- Plan to add REST/MQTT adapters and a secure aggregation backend in follow-up iterations; groundwork in this patch focuses on strengthening the contract model and making it testable. + +Notes for contributors: +- The repo now includes tests/test_contracts.py validating core primitives. Run tests with bash test.sh. +- If you extend contracts or add new primitives, add tests accordingly. - Add more adapters (e.g., Amplitude) with a consistent interface - Expand governance with versioned templates and access controls - Implement a more robust secure aggregation (secure multi-party computation or differential privacy knobs in practice) diff --git a/opengrowth_privacy_preserving_federated_/contracts.py b/opengrowth_privacy_preserving_federated_/contracts.py index 11a6e7d..09ac509 100644 --- a/opengrowth_privacy_preserving_federated_/contracts.py +++ b/opengrowth_privacy_preserving_federated_/contracts.py @@ -83,3 +83,23 @@ class GraphOfContracts: return None # return a shallow copy to avoid external mutation return {"version": entry["version"], "contract": dict(entry["contract"])} + + # --- Registry helpers for production-grade usage --- + def list_contracts(self) -> Dict[str, Dict[str, Any]]: + """Return a shallow copy of the entire registry for inspection. + + This enables lightweight auditing and debugging tooling to enumerate + known contracts without mutating the registry. + """ + return {k: {"version": v["version"], "contract": dict(v["contract"])} for k, v in self._contracts.items()} + + def unregister_contract(self, name: str) -> bool: + """Remove a contract by name from the registry. + + Returns True if the contract existed and was removed, False otherwise. + This operation is non-destructive to other contracts. + """ + if name in self._contracts: + del self._contracts[name] + return True + return False diff --git a/tests/test_contracts.py b/tests/test_contracts.py new file mode 100644 index 0000000..4a7ba1a --- /dev/null +++ b/tests/test_contracts.py @@ -0,0 +1,53 @@ +import json +from opengrowth_privacy_preserving_federated_ import contracts as c + + +def test_local_experiment_to_dict(): + le = c.LocalExperiment( + name="TestExperiment", + variables={"price": 9.99, "discount": 0.1}, + privacy_budget={"epsilon": 1.0}, + metadata={"owner": "alice"}, + ) + d = le.to_dict() + assert d["name"] == "TestExperiment" + assert d["variables"]["price"] == 9.99 + assert d["privacy_budget"]["epsilon"] == 1.0 + assert d["metadata"]["owner"] == "alice" + + +def test_shared_signals_to_dict(): + ss = c.SharedSignals( + signals={"activation_rate": 0.42, "funnel_drop": 0.15}, + provenance={"source": "mock"}, + ) + d = ss.to_dict() + assert d["signals"]["activation_rate"] == 0.42 + assert d["provenance"]["source"] == "mock" + + +def test_plan_delta_to_dict(): + pd = c.PlanDelta(delta={"activation": 1}, timestamp="2020-01-01T00:00:00Z", note="init") + d = pd.to_dict() + assert d["delta"]["activation"] == 1 + assert d["timestamp"] == "2020-01-01T00:00:00Z" + assert d["note"] == "init" + + +def test_graph_of_contracts_registry_ops(): + g = c.GraphOfContracts() + # register a contract + g.register_contract("example", {"hello": "world"}, version="1.0") + # get contract + reg = g.get_contract("example") + assert reg is not None + assert reg["version"] == "1.0" + assert reg["contract"]["hello"] == "world" + # list contracts + all_contracts = g.list_contracts() + assert "example" in all_contracts + # unregister and ensure removal + removed = g.unregister_contract("example") + assert removed is True + all_contracts = g.list_contracts() + assert "example" not in all_contracts