Skip to main content

Shared substrate primitives for swarph-mesh ecosystem (caller convention, subprocess env scrubbing, JSON-mode harness, peer-name registry, cell.yaml schema). v0.3.0 adds the cell module per substrate-doc R7 §11.1.5 (O5) cell.yaml universal-genome relocation — pure dict→Cell parser; file I/O is swarph-cli's concern.

Project description

swarph-shared

Shared substrate primitives for the swarph-mesh ecosystem. Four small, single-purpose modules consumed by every swarph-mesh component:

  • caller_convention — single source of truth for the dotted-slug caller regex used in cross-billing-path attribution joins (token_usagesubscription_usage)
  • subprocess_env — env scrubbing for claude -p (and any subprocess that must not inherit billing-path env keys); paired with subscription-setup verification
  • json_mode — JSON parsing harness with prose-extraction fallback and retry-callback contract for vendor LLMs that drift from strict-JSON output
  • peer_registry — canonical peer-name resolution against the mesh-gateway /peers endpoint, with TTL cache, static drift-mapping (KNOWN_ALIASES), and graceful gateway-unreachable degradation. Closes the framing-contagion class observed in lab-claude → lab-ovh (peer-onboarding chatter, Vector A) and drop → droplet (human-prompt shorthand, Vector B) incidents.

MIT-licensed, pure stdlib (zero runtime deps), Python 3.10+. Same authorship lineage as phawkes / fisherrao / tailcor / diebold-yilmaz / hodgex — Pierre Samson + Claude Opus.

Install

pip install swarph-shared

Usage

caller_convention — validate caller tags before attribution writes

from swarph_shared import validate_caller

# Locked convention: dotted slug, role-prefix, lowercase
validate_caller("council.judge.claude.r2")  # OK
validate_caller("orchestrator.boss")        # OK
validate_caller("Council.Judge")            # raises ValueError
validate_caller("flat_slug")                # raises ValueError (no dot)

Use this at every public surface that accepts a caller parameter. Defense-in-depth — every producer should validate, not trust upstream.

subprocess_env — keep claude -p on subscription billing

import subprocess
from swarph_shared import scrub_env_for_subprocess, verify_subscription_setup

# ONCE at boot — fail loud if subscription auth is broken
verify_subscription_setup()

# Each subprocess invocation
proc = subprocess.run(
    ["claude", "-p", "explain X", "--model", "claude-opus-4-7"],
    env={**scrub_env_for_subprocess(), "IS_SANDBOX": "1"},
    capture_output=True,
    text=True,
    timeout=120,
)

Scope (READ THIS): the denylist guards the BILLING PATH only — keeping claude -p on subscription auth (~/.claude/.credentials.json) instead of metered API. It does NOT cover general secret-leakage. Future auth-token shapes (*_TOKEN, *_SECRET, OAUTH_*, etc.) are NOT caught here — those are a separate-concern allowlist if/when added.

When new billing-relevant key shapes appear, add them to FORBIDDEN_KEYS_EXPLICIT in subprocess_env.py.

json_mode — handle vendor-drift in JSON-mode responses

from swarph_shared import parse_json, parse_json_with_retry

# Best-effort parse with prose-extraction fallback
parsed, err = parse_json('Sure, here is: {"a": 1} thanks!')
# → ({"a": 1}, None)

# Retry-on-failure harness — caller plugs in their LLM via callback
def on_retry(feedback_str: str) -> str:
    """Append feedback as a NEW [USER] turn, re-call LLM, return new text."""
    messages.append({"role": "user", "content": feedback_str})
    response = llm.invoke(messages)
    return response.text

parsed, err_class = parse_json_with_retry(initial_text, on_retry=on_retry)
# err_class is one of: None | "malformed_json" | "retry_failed"

The [USER]-turn invariant: the on_retry callback receives the feedback string and is responsible for appending it as a NEW user turn (not concatenating to the previous prompt). This multi-turn-preserving pattern is the canonical retry shape — concatenation drifts model context, new turn preserves the actual conversation.

peer_registry — validate canonical peer names before mesh DMs

from swarph_shared import validate_node_name, NotInRegistry, GatewayUnreachableError

# Resolve aliases + verify against gateway registry
canonical = validate_node_name("drop")           # → "droplet" (warns: alias resolved)
canonical = validate_node_name("droplet")        # → "droplet"
canonical = validate_node_name("ghost-peer")     # raises NotInRegistry

# Offline-tolerant for test fixtures or air-gapped dev
canonical = validate_node_name("droplet", strict=False)   # skips registry check on gateway-down

# Soft check — never raises
from swarph_shared import is_registered
if is_registered("drop"):
    send_dm(to="droplet", ...)   # the canonical resolution happens above

The convention for KNOWN_ALIASES is {alias: canonical} — the dict resolves FROM observed contagion-aliases TO registry names. Direction matters: writing {"droplet": "drop"} would silently propagate the contagion. There's a unit test (test_known_aliases_direction_is_alias_to_canonical) that anti-regression-pins this.

Two contagion vectors motivate the per-send registry check:

  • Vector A — peer-onboarding chatter. A new peer's first DMs introduce non-canonical references; receiving peers absorb. Auditable at peer-introduction. Example: lab-claude → lab-ovh.
  • Vector B — human-prompt shorthand. A human uses a friendly nickname for a peer in conversation with an AI peer; the AI absorbs without registry check. NOT auditable at peer-introduction; requires per-send registry verification at the adapter boundary. Example: drop → droplet.

Gateway-unreachable handling is two-tier per drop's PR #650 review:

  1. Cached canonical_names from prior successful query within CACHE_GRACE_SECONDS (1h) — return stale set with loud warning.
  2. No usable cache + gateway down — raise GatewayUnreachableError. Fail-loud-not-fail-silent; never default to "registry-not-checked" without explicit strict=False.

Audit-memory rationale

This package extracts patterns that emerged in cross-Claude review of the OMEGA hedge-fund-mcp codebase (lab-OVH + droplet, 2026-04 → 2026-05). The patterns matter beyond the OMEGA scope:

  • caller_convention keeps cross-billing-path attribution joins from breaking silently when a second producer adds caller-tagged rows. Originally locked in DM thread #586/#590/#592/#593 (lab+drop convergence).
  • subprocess_env prevents the silent billing-flip that the evolution_tracker.jsonl ev_an040i00v immune-catch documented (lab-orchestrator/orchestrator.py:354 had the wholesale-passthrough bug that would silently flip claude -p from subscription to API metered the moment ANTHROPIC_API_KEY entered the daemon process env).
  • json_mode.parse_json_with_retry locks the [USER]-turn retry pattern from PR #125 nit (concatenating retry feedback to the prompt drifts multi-turn semantics; appending as a new user turn preserves them).
  • peer_registry captures the framing-contagion class surfaced in two distinct incidents on 2026-05-04/05 (lab-claude, Vector A) and 2026-05-08 (drop, Vector B). The KNOWN_ALIASES dict's worked examples are documentation AND code — each entry is anti-regression-tested so the direction can never be reversed silently. The TTL-cached gateway query + graceful-degradation fallback are drop's PR #650 review carry-forwards.

These four primitives are the bottom of the swarph-mesh dependency stack — every higher-layer adapter (Gemini / DeepSeek / Claude / OpenAI / Grok) imports from here so the substrate behavior stays consistent across providers.

Versioning + dev

git clone https://github.com/darw007d/swarph-shared
cd swarph-shared
python -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
pytest

Versioning follows the same shape as the other Samson+Claude libraries: 0.1.0 initial, semver from there. Breaking changes in any of the three modules require a major bump because consumer adapters depend on the API shape.

License

MIT. Pierre Samson + Claude Opus, 2026.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

swarph_shared-0.3.0.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

swarph_shared-0.3.0-py3-none-any.whl (22.8 kB view details)

Uploaded Python 3

File details

Details for the file swarph_shared-0.3.0.tar.gz.

File metadata

  • Download URL: swarph_shared-0.3.0.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for swarph_shared-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5c375ffb29874e1c2b51eff68f19fd29a7b70be0017d7916edfb92ae1e8e056c
MD5 b4f8dfa45fdb6795db96675551474de9
BLAKE2b-256 99c83a0312cbb53a39a0d7b9b122ccb8d012448e5840ea6d174ab0707e14b0d8

See more details on using hashes here.

File details

Details for the file swarph_shared-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: swarph_shared-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 22.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for swarph_shared-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2bee91147178d5bad31811819c07fa775cc2a2ed14b0ec98d83f3b505b78d1b5
MD5 7a93658a5ab4403d5da9100ddd406684
BLAKE2b-256 0848e6f6d5d8bed8252896516864ff5e31f26116cbe71d45d84fb92eebadd67c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page