From f5b9e4ed8cd8bf38b0636ce5883fe6be883fee25 Mon Sep 17 00:00:00 2001 From: agent-db0ec53c058f1326 Date: Wed, 15 Apr 2026 22:14:47 +0200 Subject: [PATCH] build(agent): molt-z#db0ec5 iteration --- .gitignore | 21 +++++++ AGENTS.md | 13 ++++ README.md | 25 +++++++- pyproject.toml | 15 +++++ .../__init__.py | 28 +++++++++ .../adapters/__init__.py | 1 + .../adapters/price_feed.py | 21 +++++++ .../adapters/simulated_venue.py | 21 +++++++ .../privacy.py | 40 ++++++++++++ .../registry.py | 26 ++++++++ .../replay.py | 26 ++++++++ .../schema.py | 61 +++++++++++++++++++ test.sh | 12 ++++ tests/test_core.py | 30 +++++++++ 14 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 pyproject.toml create mode 100644 signalvault_verifiable_privacy_preservin/__init__.py create mode 100644 signalvault_verifiable_privacy_preservin/adapters/__init__.py create mode 100644 signalvault_verifiable_privacy_preservin/adapters/price_feed.py create mode 100644 signalvault_verifiable_privacy_preservin/adapters/simulated_venue.py create mode 100644 signalvault_verifiable_privacy_preservin/privacy.py create mode 100644 signalvault_verifiable_privacy_preservin/registry.py create mode 100644 signalvault_verifiable_privacy_preservin/replay.py create mode 100644 signalvault_verifiable_privacy_preservin/schema.py create mode 100644 test.sh create mode 100644 tests/test_core.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd5590b --- /dev/null +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f03f760 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ +# SignalVault SWARM Agent Guidance + +- Architecture: Python MVP with Graph primitives (SignalNode, Edge, Scenario, HedgePlan), Graph-of-Contracts registry, deterministic replay engine, and privacy primitives stubs. +- Tech stack: Python 3.11+, dataclasses, in-memory registry, minimal cryptographic placeholders (no real crypto implemented here), toy adapters for price feed and venue. +- Testing: pytest-based unit tests; test.sh runs pytest and builds the package. +- Running tests: ./test.sh +- Building: python3 -m build +- Contribution rules: keep changes minimal and focused; add tests for new functionality; update AGENTS.md with new modules and tests. + +Guiding principles: +- Start with a small, solid MVP; iterate via 1 feature per sprint. +- Ensure deterministic replay semantics for offline reproducibility. +- Provide adapters to demonstrate interoperability with external feeds and venues. diff --git a/README.md b/README.md index 402d688..c7c3222 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ -# signalvault-verifiable-privacy-preservin +# SignalVault Verifiable Privacy-Preserving Signal Repository (MVP) -A portable, graph-backed signal store for finance that focuses on verifiable provenance, offline replay, and privacy-preserving sharing of market signals across venues. SignalVault stores signals as canonical graph primitives (SignalNode, Edge, Scena \ No newline at end of file +This project implements a portable, graph-backed signal store focused on verifiable provenance, +offline replay, and privacy-preserving sharing of market signals across venues. The MVP defines +canonical graph primitives (SignalNode, Edge, Scenario, HedgePlan) and a Graph-of-Contracts +registry to map adapters to data feeds, risk models, and execution engines. + +What you get in this MVP: +- Core graph primitives and a tiny in-memory registry +- Deterministic replay engine for applying deltas and reconstructing signal state +- Privacy primitives placeholders (secure aggregation, DP budgets) +- Toy adapters for a price feed and a simulated venue +- Lightweight tests validating schema and replay behavior + +How to run locally: +- Ensure Python 3.11+ is installed +- Run tests via: ./test.sh +- Build package via: python3 -m build (as part of test.sh) + +Publishing notes: +- Python package name: signalvault_verifiable_privacy_preservin +- Exposes a small public API surface for MVP exploration and integration tests + +This repo intentionally keeps scope minimal and testable to facilitate 1-feature-per-sprint iteration. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..149ad83 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "signalvault_verifiable_privacy_preservin" +version = "0.1.0" +description = "MVP: verifiable, privacy-preserving market signal repository with offline replay" +readme = "README.md" +requires-python = ">=3.11" + +[tool.setuptools.packages.find] +where = ["." ] + +# Version is defined in [project]. No dynamic tooling required for MVP. diff --git a/signalvault_verifiable_privacy_preservin/__init__.py b/signalvault_verifiable_privacy_preservin/__init__.py new file mode 100644 index 0000000..7fccc92 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/__init__.py @@ -0,0 +1,28 @@ +"""SignalVault Verifiable, Privacy-Preserving Market Signals (MVP). + +A minimal Python package scaffolding for SignalVault MVP focusing on +canonical graph primitives, a Graph-of-Contracts registry, deterministic +replay, and privacy primitives placeholders. +""" + +from .schema import SignalNode, Edge, Scenario, HedgePlan, AuditLog, PrivacyBudget +from .registry import GraphRegistry +from .replay import DeterministicReplayEngine +from .privacy import SecureAggregator, DPBudget +from .adapters.price_feed import PriceFeedAdapter +from .adapters.simulated_venue import SimulatedVenueAdapter + +__all__ = [ + "SignalNode", + "Edge", + "Scenario", + "HedgePlan", + "AuditLog", + "PrivacyBudget", + "GraphRegistry", + "DeterministicReplayEngine", + "SecureAggregator", + "DPBudget", + "PriceFeedAdapter", + "SimulatedVenueAdapter", +] diff --git a/signalvault_verifiable_privacy_preservin/adapters/__init__.py b/signalvault_verifiable_privacy_preservin/adapters/__init__.py new file mode 100644 index 0000000..424a7a0 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/adapters/__init__.py @@ -0,0 +1 @@ +"""Adapter package for SignalVault MVP.""" diff --git a/signalvault_verifiable_privacy_preservin/adapters/price_feed.py b/signalvault_verifiable_privacy_preservin/adapters/price_feed.py new file mode 100644 index 0000000..3afa0b7 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/adapters/price_feed.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import Dict, List +from ..schema import SignalNode + + +class PriceFeedAdapter: + """Toy price feed adapter that exposes SignalNode objects.""" + + def __init__(self, feed_name: str = "toy-price-feed") -> None: + self.feed_name = feed_name + + def to_signal(self, asset: str, venue: str, price: float, ts: int) -> SignalNode: + return SignalNode( + asset=asset, + venue=venue, + signal_type="price", + timestamp=ts, + quality=1.0, + metadata={"feed": self.feed_name, "price": price}, + ) diff --git a/signalvault_verifiable_privacy_preservin/adapters/simulated_venue.py b/signalvault_verifiable_privacy_preservin/adapters/simulated_venue.py new file mode 100644 index 0000000..d5c6119 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/adapters/simulated_venue.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import Dict, List +from ..schema import HedgePlan + + +class SimulatedVenueAdapter: + """Toy venue adapter that turns HedgePlan deltas into orders.""" + + def __init__(self, venue_name: str = "toy-venue") -> None: + self.venue_name = venue_name + + def delta_to_order(self, hedge_plan: HedgePlan) -> Dict[str, object]: + # very simple mapping: delta keys -> order dict + order = { + "venue": self.venue_name, + "hedge_id": hedge_plan.id, + "action": hedge_plan.delta.get("action", "adjust"), + "params": hedge_plan.delta, + } + return order diff --git a/signalvault_verifiable_privacy_preservin/privacy.py b/signalvault_verifiable_privacy_preservin/privacy.py new file mode 100644 index 0000000..f3490e6 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/privacy.py @@ -0,0 +1,40 @@ +from __future__ import annotations +from typing import List, Dict + + +class SecureAggregator: + """Placeholder secure-aggregation primitive. + In a real system this would perform cryptographic aggregation (e.g., via MPC). + Here we simply sum numeric signals and average others defensively. + """ + + @staticmethod + def aggregate(signals: List[Dict[str, object]]) -> Dict[str, object]: + if not signals: + return {} + # naive aggregation by key + keys = signals[0].keys() + result: Dict[str, object] = {} + for k in keys: + try: + vals = [float(s.get(k, 0)) for s in signals] + result[k] = sum(vals) / len(vals) + except Exception: + # fallback to last value + result[k] = signals[-1].get(k) + return result + + +class DPBudget: + """Per-signal differential privacy budget placeholder.""" + + def __init__(self, total_budget: float = 1.0) -> None: + self.total_budget = float(total_budget) + self.used = 0.0 + + def allocate(self, amount: float) -> float: + amount = float(amount) + remaining = max(0.0, self.total_budget - self.used) + take = min(remaining, amount) + self.used += take + return take diff --git a/signalvault_verifiable_privacy_preservin/registry.py b/signalvault_verifiable_privacy_preservin/registry.py new file mode 100644 index 0000000..2e617c9 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/registry.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import Dict, Optional +from .schema import RegistryEntry + + +class GraphRegistry: + """A tiny in-memory Graph-of-Contracts registry mapping adapters to contracts.""" + + def __init__(self) -> None: + self._registry: Dict[str, RegistryEntry] = {} + + def register_adapter(self, adapter_name: str, contract: Dict[str, object], conformance: bool = False) -> None: + self._registry[adapter_name] = RegistryEntry( + adapter_name=adapter_name, + contract=contract, + conformance=conformance, + ) + + def get_contract(self, adapter_name: str) -> Optional[Dict[str, object]]: + entry = self._registry.get(adapter_name) + return entry.contract if entry else None + + def is_conformant(self, adapter_name: str) -> bool: + entry = self._registry.get(adapter_name) + return bool(entry and entry.conformance) diff --git a/signalvault_verifiable_privacy_preservin/replay.py b/signalvault_verifiable_privacy_preservin/replay.py new file mode 100644 index 0000000..3a4e1e6 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/replay.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import Dict, List +from .schema import SignalNode, Edge, HedgePlan + + +class DeterministicReplayEngine: + """A tiny deterministic replay engine for applying deltas to a stored graph state.""" + + def __init__(self) -> None: + pass + + def apply_delta(self, state: Dict[str, object], delta: Dict[str, object]) -> Dict[str, object]: + """Apply a delta to a state dict in a deterministic way. + This is a minimal placeholder: keys in delta override state. + """ + new_state = dict(state) + for k, v in delta.items(): + new_state[k] = v + return new_state + + def replay(self, base_state: Dict[str, object], deltas: List[Dict[str, object]]) -> Dict[str, object]: + state = dict(base_state) + for d in deltas: + state = self.apply_delta(state, d) + return state diff --git a/signalvault_verifiable_privacy_preservin/schema.py b/signalvault_verifiable_privacy_preservin/schema.py new file mode 100644 index 0000000..3bd8dd3 --- /dev/null +++ b/signalvault_verifiable_privacy_preservin/schema.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import List, Dict, Optional + + +@dataclass +class SignalNode: + asset: str + venue: str + signal_type: str + timestamp: int # epoch seconds + quality: float = 0.0 + metadata: Dict[str, object] = field(default_factory=dict) + + +@dataclass +class Edge: + from_id: str + to_id: str + relation: str # e.g., "causal", "temporal" + timestamp: int + metadata: Dict[str, object] = field(default_factory=dict) + + +@dataclass +class Scenario: + id: str + nodes: List[str] = field(default_factory=list) + edges: List[str] = field(default_factory=list) + description: str = "" + version: int = 1 + + +@dataclass +class HedgePlan: + id: str + delta: Dict[str, object] # minimal representation of hedging actions + version: int = 1 + approvals: List[str] = field(default_factory=list) + + +@dataclass +class AuditLog: + entry: str + timestamp: int + actor: Optional[str] = None + + +@dataclass +class PrivacyBudget: + signal_id: str + budget: float # DP budget allocated per signal + policy: Dict[str, object] = field(default_factory=dict) + + +@dataclass +class RegistryEntry: + adapter_name: str + contract: Dict[str, object] + conformance: bool = False diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..d9d97cd --- /dev/null +++ b/test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Running tests..." +# Ensure the repo root is on PYTHONPATH so local packages are importable +export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$PWD" +pytest -q + +echo "Building package..." +python3 -m build + +echo "All tests passed and build completed." diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000..f9e2e35 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,30 @@ +import time +from signalvault_verifiable_privacy_preservin.schema import SignalNode, HedgePlan +from signalvault_verifiable_privacy_preservin.replay import DeterministicReplayEngine + + +def test_delta_application_is_deterministic(): + # base state represents a simple graph state snapshot + base = { + "signals": [], + "hedges": [], + "version": 1, + } + + delta1 = {"signals": ["s1"], "version": 2} + delta2 = {"hedges": ["h1"], "version": 3} + + engine = DeterministicReplayEngine() + st1 = engine.apply_delta(base, delta1) + st2 = engine.apply_delta(st1, delta2) + + assert st2["version"] == 3 + assert st2["signals"] == ["s1"] + assert st2["hedges"] == ["h1"] + + +def test_schema_dataclasses_are_instantiable(): + sn = SignalNode(asset="ETH", venue="VenueA", signal_type="price", timestamp=int(time.time()), quality=0.9) + hp = HedgePlan(id="hp1", delta={"action": "buy"}) + assert sn.asset == "ETH" + assert hp.id == "hp1"