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:
- Already registered — prints the current
adapter_version,task_types, and registration timestamp so you can confirm the right version is live. - 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)