Skip to main content

The source-agnostic corroboration kernel — Resolver[T], the View contract, a pure diff, and the Corroborator. Zero runtime dependencies.

Project description

sm-resolver — the corroboration kernel

A tiny, dependency-free kernel for detecting when independent sources disagree about the same subject. Point it at two or more sources, ask them the same question, and it reports any disagreement. It is the machinery under cross-registry / cross-method divergence detection, factored out so any layer can reuse it.

Four pieces, and only the resolvers know a wire format:

  • View — the contract a claim implements: comparable() → {field: value}. Those fields are what gets compared; a None value never participates.
  • Resolver[T] — a per-source adapter: a canonical id → (Status, View). It hides one source's format (an HTTP GET, a DID resolve, a DNS lookup) and MUST NOT raise — an unreachable source is error (no claim), never a false absent.
  • diff_views — the pure diff: per-source claims → Finding list. It emits omission (present on one source, positively absent on another) and one finding per view field whose values disagree. It never learns its layer.
  • Corroborator — resolve every source, diff, and emit new findings once (deduped). Fewer than two sources → no-op.

Install

pip install sm-resolver        # zero runtime dependencies

Use

Supply a view (its fields are what you compare) and a thin resolver per source:

import asyncio
from collections.abc import Mapping
from dataclasses import dataclass
from sm_resolver import Corroborator, Status

@dataclass(frozen=True)
class RecordView:                       # your layer's view
    endpoint: str | None = None
    def comparable(self) -> Mapping[str, str | None]:
        return {"endpoint": self.endpoint}

class MyResolver:                       # your thin per-source adapter
    def __init__(self, label): self.label = label
    async def resolve(self, agent_id) -> tuple[Status, RecordView | None]:
        ...                             # query this source; normalize to RecordView

findings = asyncio.run(
    Corroborator([MyResolver("a"), MyResolver("b")]).check(["agent-1"])
)
for f in findings:
    print(f.kind, f.agent_id, f.detail)
    # endpoint agent-1 {'field': 'endpoint', 'values': {'a': 'https://real', 'b': 'https://evil'}}

Long-running? Pass on_finding= — it fires once per distinct finding across the corroborator's lifetime.

Who uses it

sm-divergence builds the reference layers on this kernel: a discovery layer (registries — NEST, the NANDA Index) and an identity layer (DID methods — did:key, did:web, Universal Resolver), each supplying its own view and thin resolvers. Capability and evidence layers are the same shape.

Design rules

  • A timeout is not a claim. An unreachable or erroring source is excluded, never counted as an omission.
  • Only present, comparable values disagree. A None field contributes nothing — an unverifiable value is not a disagreement.
  • The diff stays pure and generic — deterministic, no I/O, never raises, never learns its layer. All I/O lives in the resolvers.

Develop

make ci-local   # uv: sync → ruff → format → mypy --strict → pytest

License

MIT


First published: 2026-07-04 | Last modified: 2026-07-04

Personal research contributions aligned with Project NANDA standards. Stellarminds.ai

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

sm_resolver-0.1.0.tar.gz (36.6 kB view details)

Uploaded Source

Built Distribution

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

sm_resolver-0.1.0-py3-none-any.whl (9.7 kB view details)

Uploaded Python 3

File details

Details for the file sm_resolver-0.1.0.tar.gz.

File metadata

  • Download URL: sm_resolver-0.1.0.tar.gz
  • Upload date:
  • Size: 36.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sm_resolver-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2517272794fd0b433cef92cfd71e05976c4f9778b34eee7706e53c8858e8dff7
MD5 02df9f1be249775f3098e25dc003d28f
BLAKE2b-256 c63739891a611a0872fa191e80dad49ff68b1ee6e2c92bfd4e16c61981a13232

See more details on using hashes here.

Provenance

The following attestation bundles were made for sm_resolver-0.1.0.tar.gz:

Publisher: release.yml on Sharathvc23/sm-resolver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sm_resolver-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: sm_resolver-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sm_resolver-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d091079ff0e1b1f75fbc282d61909942ddc92ce93e260b4a7b10dd449b54e90d
MD5 2014e0e259cdba133fdc1a084260eab2
BLAKE2b-256 0dcd126190e94f687b1707260ebf333c66ee22ada960887812bff2084ec799b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for sm_resolver-0.1.0-py3-none-any.whl:

Publisher: release.yml on Sharathvc23/sm-resolver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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