build(agent): new-agents-3#dd492b iteration

This commit is contained in:
agent-dd492b85242a98c5 2026-04-19 22:56:25 +02:00
parent 781a87a0b7
commit a4ed925bef
4 changed files with 229 additions and 0 deletions

View File

@ -46,3 +46,34 @@ This repo ships a production-like MVP rather than a toy example. Expect incremen
## Migration and Evolution ## Migration and Evolution
- Contract migration: The contract registry now supports migrating a contract from an older version to a newer version. This enables adapters to evolve data schemas over time while marking the legacy version as migrated. The old version will have a migrated_to field pointing to the new version, and the new version will register its updated schema. This helps maintain backward-compatibility during MVP-driven evolution of the canonical IR. - Contract migration: The contract registry now supports migrating a contract from an older version to a newer version. This enables adapters to evolve data schemas over time while marking the legacy version as migrated. The old version will have a migrated_to field pointing to the new version, and the new version will register its updated schema. This helps maintain backward-compatibility during MVP-driven evolution of the canonical IR.
## MVP Roadmap
- Phase 0: Core interop skeleton (12 weeks)
- Solidify Objects/Morphisms/Functors with a versioned ContractRegistry (already scaffolded).
- Ship DSL seeds: LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock.
- Implement two starter adapters (rover_planner, habitat_module) connected via TLS, plus a minimal ADMM-lite solver integration.
- Phase 1: Governance & identity (23 weeks)
- Lightweight governance ledger and per-message conformance checks.
- Identity layer (DID or short-lived certs) for adapters and channels.
- Hardened contract conformance tests and deterministic replay on islanding.
- Phase 2: Cross-domain demo (23 weeks)
- Simulated two-agent end-to-end demo with a toy energy/task-allocation objective.
- SDK bindings (Python/C++) for the GoC registry and EnergiBridge-like mappings.
- Publish a minimal, stable GoC registry surface for onboarding adapters.
- Phase 3: Hardware-in-the-loop (23 weeks)
- HIL demo with Gazebo/ROS integration, KPI dashboards for convergence and delta-sync latency.
- Documentation updates and a minimal DSL tutorial for rapid prototyping.
- Success metrics
- Convergence speed to near-optimal, plan optimality gap vs centralized baseline.
- Delta-sync latency and replay accuracy during islanding/recovery.
- Adapter conformance and overall interoperability scoring.
- Deliverable artifacts
- DSL sketch docs, toy adapters, EnergiBridge mapping examples, and a publish-ready README.
If helpful, I can draft concrete toy adapters and a minimal EnergiBridge mapping to accelerate first-to-MVP progress.

View File

@ -1,5 +1,6 @@
"""CatOpt-Graph MVP package init""" """CatOpt-Graph MVP package init"""
from .core import Object, Morphism, Functor, ContractRegistry from .core import Object, Morphism, Functor, ContractRegistry
from .dsl import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock, to_json
from .admm_lite import AdmmLite from .admm_lite import AdmmLite
__all__ = [ __all__ = [
@ -7,5 +8,13 @@ __all__ = [
"Morphism", "Morphism",
"Functor", "Functor",
"ContractRegistry", "ContractRegistry",
"LocalProblem",
"SharedVariables",
"PlanDelta",
"DualVariables",
"PrivacyBudget",
"AuditLog",
"PolicyBlock",
"to_json",
"AdmmLite", "AdmmLite",
] ]

150
src/catopt_graph/dsl.py Normal file
View File

@ -0,0 +1,150 @@
"""Lightweight DSL primitives for CatOpt-Graph interoperability.
This module provides minimal, well-typed representations of the canonical
graph-interop primitives used in MVP wiring:
- LocalProblem (Object-like task description)
- SharedVariables (signals, priors, forecasts)
- PlanDelta (incremental plan changes)
- DualVariables (Lagrange multipliers / dual variables)
- PrivacyBudget (privacy budget proxy for governance)
- AuditLog (tamper-evident provenance entries)
- PolicyBlock (safety/exposure controls)
The goal is to offer a small, framework-agnostic surface that adapters can map
to/from vendor-specific representations via the EnergiBridge-like interop spine.
"""
from __future__ import annotations
from dataclasses import dataclass, asdict
from typing import Any, Dict, Optional
import json
@dataclass
class LocalProblem:
id: str
domain: str
assets: Dict[str, Any]
objective: Dict[str, Any]
constraints: Optional[Dict[str, Any]] = None
solver_hint: Optional[str] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "LocalProblem":
data = json.loads(payload)
return LocalProblem(**data)
@dataclass
class SharedVariables:
version: str
forecasts: Dict[str, Any]
priors: Optional[Dict[str, Any]] = None
encryption_schema: Optional[str] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "SharedVariables":
data = json.loads(payload)
return SharedVariables(**data)
@dataclass
class PlanDelta:
delta: Dict[str, Any]
timestamp: float
author: str
contract_id: Optional[str] = None
signature: Optional[str] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "PlanDelta":
data = json.loads(payload)
return PlanDelta(**data)
@dataclass
class DualVariables:
multipliers: Dict[str, float]
center: Optional[Dict[str, Any]] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "DualVariables":
data = json.loads(payload)
return DualVariables(**data)
@dataclass
class PrivacyBudget:
signal: str
budget: float
expiry: Optional[str] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "PrivacyBudget":
data = json.loads(payload)
return PrivacyBudget(**data)
@dataclass
class AuditLog:
entry: str
signer: str
timestamp: float
contract_id: Optional[str] = None
version: Optional[str] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "AuditLog":
data = json.loads(payload)
return AuditLog(**data)
@dataclass
class PolicyBlock:
safety: Dict[str, Any]
exposure_controls: Optional[Dict[str, Any]] = None
def to_json(self) -> str:
return json.dumps(asdict(self))
@staticmethod
def from_json(payload: str) -> "PolicyBlock":
data = json.loads(payload)
return PolicyBlock(**data)
# Simple utility to illustrate interop mapping patterns
def to_json(obj: Any) -> str:
if hasattr(obj, "to_json"):
return obj.to_json()
return json.dumps(asdict(obj))
__all__ = [
"LocalProblem",
"SharedVariables",
"PlanDelta",
"DualVariables",
"PrivacyBudget",
"AuditLog",
"PolicyBlock",
"to_json",
]

39
tests/test_dsl.py Normal file
View File

@ -0,0 +1,39 @@
import json
from catopt_graph.dsl import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock, to_json
def test_local_problem_json_roundtrip():
lp = LocalProblem(
id="lp-1",
domain="energy",
assets={"battery": 2, "solar": 5},
objective={"maximize": "util"},
constraints={"budget": 100},
solver_hint="fast-convex",
)
s = lp.to_json()
assert isinstance(s, str)
lp2 = LocalProblem.from_json(s)
assert lp2.id == lp.id
assert lp2.domain == lp.domain
def test_shared_variables_json_roundtrip():
sv = SharedVariables(version="1.0.0", forecasts={"demand": 10}, priors={"mean": 0.0})
s = sv.to_json()
sv2 = SharedVariables.from_json(s)
assert sv2.version == sv.version
def test_to_json_generic():
lp = LocalProblem(
id="lp-2",
domain="robotics",
assets={"robot": "rover-1"},
objective={"minimize": "time"},
constraints=None,
)
j = to_json(lp)
# ensures a JSON string is produced and decodable
data = json.loads(j)
assert data["id"] == lp.id