Skip to content

Adapter Validation

Every adapter must pass 13 validation rules before it can be registered with the SYNAPSE ecosystem.

Run the validator

# Validate against the built-in minimal fixture
synapse-validate --adapter my_module.MyAdapter

# Validate against all 20 standard §9 G-S06 fixtures
synapse-validate --adapter my_module.MyAdapter --all-fixtures

# Validate and check whether your MODEL_ID is already in the live registry
synapse-validate --adapter my_module.MyAdapter --check-registry

# Use a custom registry URL (self-hosted, staging, etc.)
synapse-validate --adapter my_module.MyAdapter --check-registry \
    --registry-url https://my-registry.example.com

Checking against the live registry

Pass --check-registry to query the public SYNAPSE registry after validation completes. The flag does two things:

  1. Already registered — prints the current adapter_version, task_types, and registration timestamp so you can confirm the right version is live.
  2. Not yet registered — prints the registration endpoint and a reminder to POST your manifest.

--check-registry runs even when validation fails, so you always know the registry state regardless of local test results. It never blocks the exit code — only validation failures (MUST-level rules) set a non-zero exit.

synapse-validate — MyModelAdapter
  MODEL_ID        : my-org/my-model-v1.0
  ADAPTER_VERSION : 1.0.0

Running built-in minimal fixture ...

  [PASS] built-in minimal fixture

────────────────────────────────────────────────────────────
  ✓ PASSED -- 1/1 fixtures

Registry check
  → https://registry-production-4b29.up.railway.app/v1/models

  ⚠ my-org/my-model-v1.0 is NOT registered

  To register, POST to the registry:
    POST https://registry-production-4b29.up.railway.app/v1/models
    See: https://github.com/synapse-ir/registry#registration

The 13 validation rules

Rule Level Description
INGRESS_NOT_NULL MUST ingress() must never return null or undefined
EGRESS_RETURNS_IR MUST egress() must return a valid CanonicalIR object
PROVENANCE_APPENDED MUST egress() must append exactly one ProvenanceEntry
PROVENANCE_IMMUTABLE MUST egress() must not modify any existing ProvenanceEntry
TASK_HEADER_CARRIED MUST egress() output task_header must equal original
COMPLIANCE_CARRIED MUST egress() compliance_envelope must equal original
NO_NETWORK_CALLS MUST Adapter functions must be pure — no I/O
CONFIDENCE_RANGE MUST ProvenanceEntry.confidence must be in [0.0, 1.0]
MODEL_ID_MATCH MUST ProvenanceEntry.model_id must match adapter.modelId
VERSION_SEMVER MUST adapter_version must be valid semver
LATENCY_POSITIVE SHOULD latency_ms should be > 0
COST_NON_NEGATIVE SHOULD cost_usd, if present, should be >= 0.0
CONTENT_PRESERVED SHOULD payload.content should not be mutated by egress

Common failures and fixes

PROVENANCE_IMMUTABLE

# Wrong
ir.provenance[0].confidence = 0.9

# Right
updated.provenance.append(self.build_provenance(confidence=0.9, latency_ms=latency_ms))

TASK_HEADER_CARRIED

updated = original_ir.clone()  # clone() performs deep copy, carries task_header

Running fixtures in tests

Use assert_valid_on() to run a specific fixture through your adapter inside a test suite:

import pytest
from synapse_sdk.testing import AdapterValidator
from synapse_sdk.testing.fixtures import ALL_FIXTURES
from my_model.my_adapter import MyAdapter

@pytest.mark.parametrize("fixture", ALL_FIXTURES)
def test_all_fixtures(fixture):
    AdapterValidator(MyAdapter()).assert_valid_on(fixture)