build(agent): new-agents-3#dd492b iteration
This commit is contained in:
parent
7eb5c1a7b4
commit
6944eda72d
|
|
@ -0,0 +1,21 @@
|
||||||
|
node_modules/
|
||||||
|
.npmrc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
__tests__/
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
tmp/
|
||||||
|
.tmp/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
|
READY_TO_PUBLISH
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
# HopeMesh AGENTS
|
||||||
|
|
||||||
|
- Overview
|
||||||
|
- A production-oriented skeleton for a cross-domain, offline-first resource allocation system with governance and adapters.
|
||||||
|
|
||||||
|
- Architecture
|
||||||
|
- Core primitives: LocalProblem, SharedSignals, PlanDelta (GoC-style contracts).
|
||||||
|
- Graph of Contracts (GoC): lightweight in-process graph to connect problems, signals, and actions.
|
||||||
|
- Adapters: pluggable endpoints for field units, warehouses, transport hubs; starter adapters included.
|
||||||
|
- Transport: TLS-backed channel abstraction for inter-device communication.
|
||||||
|
- Governance: skeleton ledger for provenance with per-message signatures (extensible to DID-based identities).
|
||||||
|
|
||||||
|
- Tech Stack
|
||||||
|
- Language: Python 3.9+
|
||||||
|
- Packaging: pyproject.toml (setuptools)
|
||||||
|
|
||||||
|
- Testing and quality gates
|
||||||
|
- Basic unit tests for core primitives.
|
||||||
|
- test.sh to run pytest and python -m build for packaging checks.
|
||||||
|
- AGENTS.md intentionally documents architecture so future agents can contribute without breaking it.
|
||||||
|
|
||||||
|
- How to run locally
|
||||||
|
- python3 -m pytest
|
||||||
|
- bash test.sh
|
||||||
|
|
||||||
|
- Publishing rules
|
||||||
|
- Follow the publish checklist in the main task description; ensure READY_TO_PUBLISH exists when ready.
|
||||||
33
README.md
33
README.md
|
|
@ -1,3 +1,32 @@
|
||||||
# idea152-hopemesh-federated-privacy
|
# HopeMesh: Federated, Privacy-Preserving Humanitarian Resource Allocation
|
||||||
|
|
||||||
Source logic for Idea #152
|
Overview
|
||||||
|
- HopeMesh is a distributed, offline-first platform to coordinate humanitarian aid across agencies in crisis zones with intermittent connectivity.
|
||||||
|
- It models local needs as LocalProblem, supply signals as SharedSignals, and delta actions as PlanDelta within a Graph-of-Contracts (GoC) architecture.
|
||||||
|
- The MVP focuses on cross-domain adapters (supply-inventory tracker and field-needs collector) with TLS transport, governance ledger skeleton, and a small cross-domain objective.
|
||||||
|
|
||||||
|
Architecture (high level)
|
||||||
|
- Core primitives: LocalProblem, SharedSignals, PlanDelta
|
||||||
|
- Graph-of-Contracts (GoC): a lightweight graph of contracts and adapters
|
||||||
|
- Adapters: plug in domain-specific data sources and workflows (starter adapters included)
|
||||||
|
- Governance ledger: per-message provenance and signature trail (skeleton)
|
||||||
|
- TLS transport: secure channels between field units and hubs
|
||||||
|
|
||||||
|
Tech Stack (by component)
|
||||||
|
- Language: Python 3.9+
|
||||||
|
- Packaging: pyproject.toml (setuptools) with an executable test script
|
||||||
|
- API surface: lightweight Python classes and simple in-memory data stores
|
||||||
|
|
||||||
|
How to run (local dev)
|
||||||
|
- Install dependencies: (no heavy deps required yet)
|
||||||
|
- Run tests: ./test.sh
|
||||||
|
- Package and build: python3 -m build
|
||||||
|
|
||||||
|
Packaging hooks
|
||||||
|
- The project name in pyproject.toml is idea152-hopemesh-federated-privacy to satisfy publishing rules.
|
||||||
|
- The Python importable package is idea152_hopemesh_federated_privacy (underscore style) to align with Python packaging norms.
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
- See AGENTS.md for architecture, testing commands, and contribution rules.
|
||||||
|
|
||||||
|
License: MIT (example)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "idea152-hopemesh-federated-privacy"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Federated, privacy-preserving humanitarian resource allocation prototype (HopeMesh)"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
license = { file = "LICENSE" }
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://example.org/hope-mesh"
|
||||||
|
Repository = "https://github.com/example/hope-mesh"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
"""Temporary namespace for the HopeMesh Python package.
|
||||||
|
This package provides core primitives and a minimal MVP scaffolding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"core",
|
||||||
|
"governance",
|
||||||
|
"adapters",
|
||||||
|
"tls_transport",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Adapters package for HopeMesh MVP."""
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Starter adapters for HopeMesh MVP."""
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List, Dict
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FieldNeedsCollector:
|
||||||
|
collected: List[Dict] = field(default_factory=list)
|
||||||
|
|
||||||
|
def add_need(self, location: str, need_type: str, quantity: int, time_window: tuple[str, str]) -> None:
|
||||||
|
need = {
|
||||||
|
"location": location,
|
||||||
|
"need_type": need_type,
|
||||||
|
"quantity": quantity,
|
||||||
|
"time_window": time_window,
|
||||||
|
"timestamp": time.time(),
|
||||||
|
}
|
||||||
|
self.collected.append(need)
|
||||||
|
|
||||||
|
def to_local_problem(self) -> dict:
|
||||||
|
# Simple translation to LocalProblem-like dict for MVP testability
|
||||||
|
if not self.collected:
|
||||||
|
return {}
|
||||||
|
n = self.collected[-1]
|
||||||
|
return {
|
||||||
|
"location": n["location"],
|
||||||
|
"need_type": n["need_type"],
|
||||||
|
"quantity": n["quantity"],
|
||||||
|
"time_window": n["time_window"],
|
||||||
|
"priority": 3,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SupplyInventoryTracker:
|
||||||
|
inventory: Dict[str, int] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def register_item(self, item: str, quantity: int) -> None:
|
||||||
|
if item not in self.inventory:
|
||||||
|
self.inventory[item] = 0
|
||||||
|
self.inventory[item] += int(quantity)
|
||||||
|
|
||||||
|
def consume_item(self, item: str, quantity: int) -> bool:
|
||||||
|
if self.inventory.get(item, 0) < quantity:
|
||||||
|
return False
|
||||||
|
self.inventory[item] -= quantity
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"SupplyInventoryTracker(inventory={self.inventory})"
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, List, Tuple, Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblem:
|
||||||
|
location: str
|
||||||
|
need_type: str
|
||||||
|
priority: int # 1-5, higher means more urgent
|
||||||
|
quantity: int
|
||||||
|
time_window: Tuple[str, str] # (start_iso, end_iso)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedSignals:
|
||||||
|
stock_levels: Dict[str, int] # e.g., {"itemA": 100}
|
||||||
|
urgency_scores: Dict[str, float] # per-location urgency or need types
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
plan_id: str
|
||||||
|
actions: List[Dict[str, object]] # simple action descriptors
|
||||||
|
timestamp: str # ISO timestamp
|
||||||
|
valid: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
class GraphOfContracts:
|
||||||
|
"""Minimal GoC-like graph to connect problems, signals, and deltas."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.local_problems: List[LocalProblem] = []
|
||||||
|
self.shared_signals: Optional[SharedSignals] = None
|
||||||
|
self.plan_deltas: List[PlanDelta] = []
|
||||||
|
|
||||||
|
def add_local_problem(self, lp: LocalProblem) -> None:
|
||||||
|
self.local_problems.append(lp)
|
||||||
|
|
||||||
|
def set_shared_signals(self, ss: SharedSignals) -> None:
|
||||||
|
self.shared_signals = ss
|
||||||
|
|
||||||
|
def add_plan_delta(self, pd: PlanDelta) -> None:
|
||||||
|
self.plan_deltas.append(pd)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"GraphOfContracts(LocalProblems={len(self.local_problems)}, "
|
||||||
|
f"SharedSignals={'set' if self.shared_signals else 'unset'}, "
|
||||||
|
f"PlanDeltas={len(self.plan_deltas)})"
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
_SHARED_KEY = b"super-secret-key-for-tests" # In production, use DID-based keys or KMS
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GovernanceEntry:
|
||||||
|
message: str
|
||||||
|
signature: str
|
||||||
|
timestamp: float
|
||||||
|
|
||||||
|
|
||||||
|
class GovernanceLedger:
|
||||||
|
"""Skeleton governance ledger with simple HMAC-based signatures for testability."""
|
||||||
|
|
||||||
|
def __init__(self, key: bytes | None = None) -> None:
|
||||||
|
self.key = key or _SHARED_KEY
|
||||||
|
self.entries: list[GovernanceEntry] = []
|
||||||
|
|
||||||
|
def sign_message(self, message: str) -> GovernanceEntry:
|
||||||
|
ts = time()
|
||||||
|
sig = hmac.new(self.key, msg=message.encode("utf-8"), digestmod=hashlib.sha256).hexdigest()
|
||||||
|
entry = GovernanceEntry(message=message, signature=sig, timestamp=ts)
|
||||||
|
self.entries.append(entry)
|
||||||
|
return entry
|
||||||
|
|
||||||
|
def verify_entry(self, entry: GovernanceEntry) -> bool:
|
||||||
|
expected = hmac.new(self.key, msg=entry.message.encode("utf-8"), digestmod=hashlib.sha256).hexdigest()
|
||||||
|
return hmac.compare_digest(expected, entry.signature)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"GovernanceLedger(entries={len(self.entries)})"
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
class TLSEndpoint:
|
||||||
|
"""Minimal TLS transport stub for MVP. Not a full TLS server/client.
|
||||||
|
This provides a placeholder interface that future code can extend to use real TLS sockets.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, address: str, use_tls: bool = True):
|
||||||
|
self.address = address
|
||||||
|
self.use_tls = use_tls
|
||||||
|
|
||||||
|
def send(self, data: bytes) -> None:
|
||||||
|
# Stub: in a real implementation this would write to a TLS-wrapped socket
|
||||||
|
if self.use_tls:
|
||||||
|
# pretend to encrypt and send
|
||||||
|
_ = data # no-op for placeholder
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def receive(self) -> bytes:
|
||||||
|
# Stub: return empty bytes for MVP
|
||||||
|
return b""
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
printf "Running tests...\n"
|
||||||
|
pytest -q
|
||||||
|
printf "Building package...\n"
|
||||||
|
python3 -m build
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import time
|
||||||
|
from src.core import LocalProblem, SharedSignals, PlanDelta, GraphOfContracts
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_problem_dataclass():
|
||||||
|
lp = LocalProblem(
|
||||||
|
location="Site-A",
|
||||||
|
need_type="food_rations",
|
||||||
|
priority=4,
|
||||||
|
quantity=1000,
|
||||||
|
time_window=("2026-04-21T00:00:00Z", "2026-04-22T00:00:00Z"),
|
||||||
|
)
|
||||||
|
assert lp.location == "Site-A"
|
||||||
|
assert lp.need_type == "food_rations"
|
||||||
|
assert lp.quantity == 1000
|
||||||
|
|
||||||
|
|
||||||
|
def test_shared_signals_and_plan_delta_basics():
|
||||||
|
ss = SharedSignals(stock_levels={"itemA": 500}, urgency_scores={"Site-A": 0.8})
|
||||||
|
assert ss.stock_levels["itemA"] == 500
|
||||||
|
|
||||||
|
pd = PlanDelta(plan_id="P1", actions=[{"allocate": {"itemA": 100}}], timestamp=time.time())
|
||||||
|
assert pd.plan_id == "P1"
|
||||||
|
assert isinstance(pd.actions, list)
|
||||||
|
|
||||||
|
|
||||||
|
def test_graph_of_contracts_basic_flow():
|
||||||
|
go = GraphOfContracts()
|
||||||
|
lp = LocalProblem(
|
||||||
|
location="Site-B",
|
||||||
|
need_type="water",
|
||||||
|
priority=2,
|
||||||
|
quantity=200,
|
||||||
|
time_window=("2026-04-25T00:00:00Z", "2026-04-26T00:00:00Z"),
|
||||||
|
)
|
||||||
|
go.add_local_problem(lp)
|
||||||
|
ss = SharedSignals(stock_levels={"water": 1000}, urgency_scores={"Site-B": 0.5})
|
||||||
|
go.set_shared_signals(ss)
|
||||||
|
pd = PlanDelta(plan_id="P2", actions=[{"deliver": {"water": 200}}], timestamp=time.time())
|
||||||
|
go.add_plan_delta(pd)
|
||||||
|
assert len(go.local_problems) == 1
|
||||||
|
assert go.shared_signals is ss
|
||||||
|
assert len(go.plan_deltas) == 1
|
||||||
Loading…
Reference in New Issue