From 7af52d7e886587ea866bcf355b4c842a42ee0aa1 Mon Sep 17 00:00:00 2001 From: agent-db0ec53c058f1326 Date: Wed, 15 Apr 2026 21:00:46 +0200 Subject: [PATCH] build(agent): molt-z#db0ec5 iteration --- README.md | 7 +++++++ core/contracts.py | 28 +++++++++++++++++++++++++--- core/graph_of_contracts.py | 32 ++++++++++++++++++++++++++++++++ docs/dsl_sketch.md | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 core/graph_of_contracts.py create mode 100644 docs/dsl_sketch.md diff --git a/README.md b/README.md index e28db53..f1139b2 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,10 @@ How to run tests Notes - This MVP focuses on minimal, well-scoped components to enable end-to-end interoperability with adapters and the ADMM-lite solver. - See core/bridge.py for the canonical mapping primitives and tests for contract registry and bridge in tests/. + +## MVP Enhancements +- Added a lightweight Graph-of-Contracts in core/graph_of_contracts.py to map adapters to versioned contracts. +- Added a minimal thread-safe enhancement to ContractRegistry (core/contracts.py) for concurrency safety. +- Added a minimal DSL sketch documenting the LocalProblem/SharedVariables/PlanDelta data contracts (docs/dsl_sketch.md). +- Added a minimal in-repo DSL documentation and a sample Graph-of-Contracts scaffold to accelerate adapter onboarding. +- Documentation and a ready-to-publish readiness baton in READY_TO_PUBLISH (to signal MVP stability when publishing). diff --git a/core/contracts.py b/core/contracts.py index 081c9dd..ac4e26e 100644 --- a/core/contracts.py +++ b/core/contracts.py @@ -1,6 +1,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, Dict +import threading @dataclass @@ -55,13 +56,34 @@ class ContractRegistry: """ def __init__(self) -> None: + # In-memory, versioned registry with simple thread-safety for MVP self._store: Dict[str, Dict[str, ContractDefinition]] = {} + self._lock = threading.RLock() def add_contract(self, name: str, version: str, schema: Dict[str, Any]) -> None: - self._store.setdefault(name, {})[version] = ContractDefinition(name, version, schema) + with self._lock: + self._store.setdefault(name, {})[version] = ContractDefinition(name, version, schema) def get_contract(self, name: str, version: str) -> ContractDefinition | None: - return self._store.get(name, {}).get(version) + with self._lock: + return self._store.get(name, {}).get(version) def list_contracts(self) -> Dict[str, Dict[str, ContractDefinition]]: - return self._store + with self._lock: + # Return a shallow copy to avoid external mutation + return {k: dict(v) for k, v in self._store.items()} + + # Convenience helpers for MVP tooling + def has_contract(self, name: str, version: str) -> bool: + with self._lock: + return version in self._store.get(name, {}) + + def remove_contract(self, name: str, version: str) -> bool: + with self._lock: + if name in self._store and version in self._store[name]: + del self._store[name][version] + # Clean up empty dicts + if not self._store[name]: + del self._store[name] + return True + return False diff --git a/core/graph_of_contracts.py b/core/graph_of_contracts.py new file mode 100644 index 0000000..5ce76cc --- /dev/null +++ b/core/graph_of_contracts.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import Dict, Optional + +from .contracts import ContractDefinition + + +class GraphOfContracts: + """Lightweight registry graph mapping adapters to contract versions. + + This is intentionally minimal: it stores per-adapter contracts organized by + contract name -> version -> ContractDefinition, enabling plug-and-play across + domain adapters while keeping a canonical reference per adapter. + """ + + def __init__(self) -> None: + # adapter_name -> contract_name -> version -> ContractDefinition + self._graph: Dict[str, Dict[str, Dict[str, ContractDefinition]]] = {} + + def register(self, adapter: str, contract_name: str, version: str, schema: Dict[str, object]) -> None: + self._graph.setdefault(adapter, {}) + self._graph[adapter].setdefault(contract_name, {})[version] = ContractDefinition( + name=contract_name, + version=version, + schema=schema, + ) + + def get_contract(self, adapter: str, contract_name: str, version: str) -> Optional[ContractDefinition]: + return self._graph.get(adapter, {}).get(contract_name, {}).get(version) + + def list_adapter_contracts(self, adapter: str) -> Dict[str, Dict[str, ContractDefinition]]: + return self._graph.get(adapter, {}) diff --git a/docs/dsl_sketch.md b/docs/dsl_sketch.md new file mode 100644 index 0000000..101f77c --- /dev/null +++ b/docs/dsl_sketch.md @@ -0,0 +1,35 @@ +# DSL Sketch: Core CatOpt-Graph Primitives + +This document provides a minimal sketch for the DSL that describes LocalProblems, +SharedVariables, PlanDelta, and related contracts used by adapters and the ADMM-lite +core. + +LocalProblem +- asset_id: identifier of the local optimization problem on the device +- payload: dictionary containing domain-specific problem data (e.g., objective terms, constraints) + +SharedVariables +- iter_id: iteration counter for synchronization rounds +- values: map of signals that are shared across the mesh (e.g., dual variables, summaries) + +DualVariables +- iter_id: iteration counter +- values: dual variables per shared signal + +PlanDelta +- iter_id: iteration counter +- delta: aggregated changes to be applied to local plans + +PrivacyBudget +- budget_id: identifier for privacy budget instance +- limits: per-signal DP budgets or leakage limits + +AuditLog +- entry_id: unique log entry identifier +- payload: audit metadata for reconciliation steps + +Notes +- All messages should include per-message metadata (version, timestamp, nonce, crypto-tag). +- Identity should be established via DIDs or short-lived certificates; transport should be TLS-secured. + +This sketch intentionally stays minimal to accelerate MVP integration.