build(agent): new-agents-2#7e3bbc iteration

This commit is contained in:
agent-7e3bbc424e07835b 2026-04-20 15:29:41 +02:00
parent 9c930445b3
commit 0bf6a5bd06
15 changed files with 359 additions and 7 deletions

View File

@ -19,3 +19,8 @@ How to run locally:
This is a minimal, production-oriented MVP designed to be extended by follow-up iterations.
Documentation: See docs/CRISISPULSE_MVP_SPEC.md for a concrete MVP blueprint focused on a two-adapter (solar and water) setup and offline delta-sync workflows.
Package integration:
- Python package name: idea168_crisispulse_federated_resource
- Version: 0.1.0 (consistent with pyproject.toml)
- Exposes core primitives under idea168_crisispulse_federated_resource.core

View File

@ -7,6 +7,7 @@ name = "idea168_crisispulse_federated_resource"
description = "CrisisPulse: Federated Resource Orchestration for Disaster-Relief Camp Networks MVP"
readme = "README.md"
version = "0.1.0"
requires-python = ">=3.11"
authors = [{name = "OpenCode Robot", email = "engineer@example.com"}]
license = {text = "MIT"}
classifiers = [

16
setup.py Normal file
View File

@ -0,0 +1,16 @@
import setuptools
setuptools.setup(
name="idea168_crisispulse_federated_resource",
version="0.1.0",
packages=setuptools.find_packages("src"),
package_dir={"": "src"},
description="CrisisPulse MVP: Federated Resource Orchestration for Disaster-Relief Camp Networks",
long_description="",
long_description_content_type="text/markdown",
url="",
author="OpenCode Robot",
license="MIT",
python_requires=">=3.11",
install_requires=[],
)

View File

@ -1,10 +1,14 @@
"""CrisisPulse Federated Resource (package init)"""
"""idea168_crisispulse_federated_resource
A production-oriented MVP scaffold for CrisisPulse fellowing the idea168 spec.
This package exposes a minimal, well-typed core to validate the local ledger,
delta-sync, contract registry, adapters, governance, and privacy primitives
used in unit tests.
"""
__all__ = [
"ledger",
"contract_registry",
"adapters",
"governance",
"privacy",
"sim",
"core",
"__version__",
]
__version__ = "0.1.0"

View File

@ -0,0 +1,10 @@
"""Core primitives for CrisisPulse MVP"""
__all__ = [
"ledger",
"contract_registry",
"adapters",
"governance",
"privacy",
"sim",
]

View File

@ -0,0 +1,45 @@
"""Base adapters and two sample starter adapters (Solar, WaterPurifier)."""
from __future__ import annotations
from typing import Any, Dict
from .ledger import LocalLedger
class BaseAdapter:
name: str = "base-adapter"
def export_resources(self, ledger: LocalLedger) -> Dict[str, Any]:
# Return a minimal perspective of resources available
return {
"adapter": self.name,
"resources": {},
}
class SolarAdapter(BaseAdapter):
name = "solar-adapter"
def export_resources(self, ledger: LocalLedger) -> Dict[str, Any]:
# Expose a tiny LocalResourcePlan and a forecast
plan = {
"domain": "energy",
"allocation": {"capacity_kw": 50},
}
forecast = {"domain": "energy", "demand_kw": 40}
return {
"adapter": self.name,
"resources": {"LocalResourcePlan": plan, "SharedForecast": forecast},
}
class WaterPurifierAdapter(BaseAdapter):
name = "water-purifier-adapter"
def export_resources(self, ledger: LocalLedger) -> Dict[str, Any]:
plan = {"domain": "water", "allocation_liters": 1000}
forecast = {"domain": "water", "demand_liters": 900}
return {
"adapter": self.name,
"resources": {"LocalResourcePlan": plan, "SharedForecast": forecast},
}

View File

@ -0,0 +1,26 @@
"""Graph-of-Contracts: versioned schemas registry (MVP)."""
from __future__ import annotations
from typing import Any, Dict, Tuple
class GraphOfContracts:
def __init__(self) -> None:
# contract_name -> version -> schema
self._registry: Dict[str, Dict[int, Dict[str, Any]]] = {}
def register(self, contract_name: str, version: int, schema: Dict[str, Any]) -> None:
self._registry.setdefault(contract_name, {})[version] = schema
def get(self, contract_name: str, version: int) -> Dict[str, Any]:
versions = self._registry.get(contract_name, {})
if version not in versions:
raise KeyError(f"contract {contract_name} version {version} not found")
return versions[version]
def latest_version(self, contract_name: str) -> Tuple[int, Dict[str, Any]]:
versions = self._registry.get(contract_name, {})
if not versions:
raise KeyError(f"contract {contract_name} has no versions")
ver = max(versions.keys())
return ver, versions[ver]

View File

@ -0,0 +1,37 @@
"""Tamper-evident governance ledger (MVP)."""
from __future__ import annotations
import hmac
import time
from hashlib import sha256
from typing import Any, Dict, List
class GovernanceLedger:
def __init__(self, signer_key: str) -> None:
self._signer_key = signer_key.encode("utf-8")
self._entries: List[Dict[str, Any]] = []
def sign_event(self, event: Dict[str, Any]) -> Dict[str, Any]:
payload = sha256(_serialize(event).encode("utf-8")).hexdigest()
signature = hmac.new(self._signer_key, payload.encode("utf-8"), sha256).hexdigest()
entry = {
"ts": int(time.time()),
"event": event,
"signature": signature,
}
self._entries.append(entry)
return entry
def verify_event(self, entry: Dict[str, Any]) -> bool:
event = entry.get("event")
sig = entry.get("signature")
if event is None or sig is None:
return False
payload = sha256(_serialize(event).encode("utf-8")).hexdigest()
expected = hmac.new(self._signer_key, payload.encode("utf-8"), sha256).hexdigest()
return hmac.compare_digest(expected, sig)
def _serialize(obj: object) -> str:
import json
return json.dumps(obj, sort_keys=True, separators=(",", ":"))

View File

@ -0,0 +1,62 @@
"""Local ledger with delta-sync primitives (MVP).
This is a tiny, self-contained in-memory ledger designed for tests. It
implements:
- Adding entries
- Delta application with a simple tag-based signature
- Merkle-root-like state digest for basic integrity checks
"""
from __future__ import annotations
import hashlib
import json
import time
from typing import Any, Dict, Optional
def _serialize(obj: Any) -> str:
return json.dumps(obj, sort_keys=True, separators=(",", ":"))
class LocalLedger:
def __init__(self) -> None:
# entry_id -> {"data": ..., "version": int, "ts": int}
self._entries: Dict[str, Dict[str, Any]] = {}
self._version_counter: int = 0
def add_entry(self, entry_id: str, data: Dict[str, Any]) -> None:
self._version_counter += 1
self._entries[entry_id] = {
"data": data,
"version": self._version_counter,
"ts": int(time.time()),
}
def get_entry(self, entry_id: str) -> Optional[Dict[str, Any]]:
return self._entries.get(entry_id)
def apply_delta(self, delta: Dict[str, Any]) -> None:
# delta schema: {"entry_id": str, "changes": dict, "tags": [str], "signature": str}
entry_id = delta.get("entry_id")
changes = delta.get("changes", {})
if not entry_id:
raise ValueError("delta must contain entry_id")
base = self._entries.get(entry_id, {"data": {}, "version": 0, "ts": 0})["data"]
if not isinstance(base, dict):
base = {}
# naive merge
merged = {**base, **changes}
self.add_entry(entry_id, merged)
def merkle_root(self) -> str:
# Simple Merkle-like digest: hash of sorted entries json
items = []
for k in sorted(self._entries.keys()):
items.append(_serialize({k: self._entries[k]}))
payload = "|".join(items)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
def snapshot(self) -> Dict[str, Any]:
# Return a shallow copy suitable for tests
return {k: v.copy() for k, v in self._entries.items()}

View File

@ -0,0 +1,28 @@
"""Privacy-preserving summaries (MVP): simple per-entry budgets and secure aggregation."""
from __future__ import annotations
from typing import Dict
class PrivacyBudget:
def __init__(self, per_entry_budget: int) -> None:
self.per_entry_budget = per_entry_budget
self.usage: Dict[str, int] = {}
def spend(self, entry_id: str, amount: int) -> None:
self.usage[entry_id] = self.usage.get(entry_id, 0) + amount
def remaining(self, entry_id: str) -> int:
spent = self.usage.get(entry_id, 0)
return max(0, self.per_entry_budget - spent)
def secure_aggregate(data: Dict[str, int], budget: PrivacyBudget) -> Dict[str, int]:
# Very simple per-key DP-like masking: cap contributions by budget per key
result: Dict[str, int] = {}
for k, v in data.items():
rem = budget.remaining(k)
take = min(v, rem)
budget.spend(k, take)
result[k] = take
return result

View File

@ -0,0 +1,19 @@
"""Lightweight simulation helpers for delta-sync and multi-domain co-simulation (MVP)."""
from __future__ import annotations
from typing import Any, Dict
def simulate_delta_sync(source: Dict[str, Any], target: Dict[str, Any]) -> Dict[str, Any]:
"""Merge two domain plans in a deterministic way.
This is a very small helper suitable for tests and toy demos. It performs a
simple deep merge where values from source override target when keys collide.
"""
merged = dict(target)
for k, v in source.items():
if isinstance(v, dict) and isinstance(merged.get(k), dict):
merged[k] = {**merged[k], **v}
else:
merged[k] = v
return merged

26
tests/test_adapters.py Normal file
View File

@ -0,0 +1,26 @@
import os
import sys
import pathlib
ROOT = pathlib.Path(__file__).resolve().parents[1]
SRC = str(ROOT / "src")
sys.path.insert(0, SRC)
from idea168_crisispulse_federated_resource.core.adapters import SolarAdapter, WaterPurifierAdapter
from idea168_crisispulse_federated_resource.core.ledger import LocalLedger
def test_solar_adapter_exports_resources():
ledger = LocalLedger()
adj = SolarAdapter()
out = adj.export_resources(ledger)
assert out["adapter"] == "solar-adapter"
assert "LocalResourcePlan" in out["resources"]
def test_water_purifier_adapter_exports_resources():
ledger = LocalLedger()
adj = WaterPurifierAdapter()
out = adj.export_resources(ledger)
assert out["adapter"] == "water-purifier-adapter"
assert "SharedForecast" in out["resources"]

View File

@ -0,0 +1,20 @@
import os
import sys
import pathlib
ROOT = pathlib.Path(__file__).resolve().parents[1]
SRC = str(ROOT / "src")
sys.path.insert(0, SRC)
from idea168_crisispulse_federated_resource.core.contract_registry import GraphOfContracts
def test_contract_registry_versions():
reg = GraphOfContracts()
reg.register("LocalResourcePlan", 1, {"schema": {"type": "object"}})
reg.register("LocalResourcePlan", 2, {"schema": {"type": "object", "properties": {"domain": {"type": "string"}}}})
ver_schema = reg.get("LocalResourcePlan", 2)
assert isinstance(ver_schema, dict)
ver, schema = reg.latest_version("LocalResourcePlan")
assert ver == 2
assert isinstance(schema, dict)

33
tests/test_ledger.py Normal file
View File

@ -0,0 +1,33 @@
import os
import sys
# Ensure tests can import the src package layout when running from repo root
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
SRC = os.path.join(ROOT, "src")
sys.path.insert(0, SRC)
from idea168_crisispulse_federated_resource.core.ledger import LocalLedger
def test_ledger_add_and_snapshot_and_merkle():
ledger = LocalLedger()
ledger.add_entry("camp1", {"resources": {"energy": 100}})
ledger.add_entry("camp2", {"resources": {"water": 200}})
snap = ledger.snapshot()
assert "camp1" in snap and "camp2" in snap
root = ledger.merkle_root()
assert isinstance(root, str) and len(root) == 64
def test_ledger_apply_delta():
ledger = LocalLedger()
ledger.add_entry("camp1", {"resources": {"energy": 50}})
delta = {
"entry_id": "camp1",
"changes": {"resources": {"energy": 75}},
"tags": ["test"],
"signature": "signature-placeholder",
}
ledger.apply_delta(delta)
entry = ledger.get_entry("camp1")
assert entry is not None
assert entry["data"]["resources"]["energy"] == 75

20
tests/test_privacy.py Normal file
View File

@ -0,0 +1,20 @@
import os
import sys
import pathlib
ROOT = pathlib.Path(__file__).resolve().parents[1]
SRC = str(ROOT / "src")
sys.path.insert(0, SRC)
from idea168_crisispulse_federated_resource.core.privacy import PrivacyBudget, secure_aggregate
def test_privacy_budget_and_aggregate():
budget = PrivacyBudget(per_entry_budget=5)
data = {"camp1": 3, "camp2": 4}
masked = secure_aggregate(data, budget)
assert all(v <= 5 for v in masked.values())
# second call uses remaining budgets
more = {"camp1": 3, "camp2": 2}
masked2 = secure_aggregate(more, budget)
assert isinstance(masked2, dict)