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