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.
It is the reference implementation of the IETF draft Multi-Source
Corroboration for AI Agent Discovery
(draft-chandra-agent-registry-corroboration-00).
Five pieces, and only the resolvers know a wire format:
View— the contract a claim implements:comparable() → {field: value}. Those fields are what gets compared; aNonevalue 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 iserror(no claim), never a falseabsent. It MAY expose avantage(a network perspective).Claim— one source's answer from one vantage at one instant.diff_claims— the pure diff: a sweep's claims →Findinglist. It emitsomission(present on one source, positively absent on another),source_equivocation(one source's vantages disagree with each other), and one finding per view field whose values disagree across sources. It never learns its layer.Corroborator— resolve every(source, vantage), diff, apply confirmation, and return oneSweepResultper subject: a verdict (AGREE/DIVERGENT/INSUFFICIENT), the claims, and the findings. Fewer than two decisive claims →INSUFFICIENT.
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
results = asyncio.run(
Corroborator([MyResolver("a"), MyResolver("b")]).check(["agent-1"])
)
for r in results:
print(r.agent_id, r.verdict) # agent-1 DIVERGENT
for f in r.findings:
print(" ", f.kind, f.confirmation, f.detail)
# endpoint suspected {'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 — and staleness_window_s= to promote a re-observed
finding from suspected to confirmed.
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
Nonefield 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
First published: 2026-07-04 | Last modified: 2026-07-04
Personal research contributions aligned with Project NANDA standards. Stellarminds.ai
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sm_resolver-0.2.0.tar.gz.
File metadata
- Download URL: sm_resolver-0.2.0.tar.gz
- Upload date:
- Size: 41.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f15c80755f94f86d1d7750211d754e128bcd73a2a9edcf162a927da4a599678
|
|
| MD5 |
e5c9da6b25c9aa74b84f36b11ef5562a
|
|
| BLAKE2b-256 |
0ee721f28883b0a7167a10631784f93f3f1209ac73e491cc5b7e6e7fca889fb8
|
Provenance
The following attestation bundles were made for sm_resolver-0.2.0.tar.gz:
Publisher:
release.yml on Sharathvc23/sm-resolver
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sm_resolver-0.2.0.tar.gz -
Subject digest:
1f15c80755f94f86d1d7750211d754e128bcd73a2a9edcf162a927da4a599678 - Sigstore transparency entry: 2075175791
- Sigstore integration time:
-
Permalink:
Sharathvc23/sm-resolver@2c70afdb0fa4e11384fe0c46281fb66ff5a94148 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Sharathvc23
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2c70afdb0fa4e11384fe0c46281fb66ff5a94148 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sm_resolver-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sm_resolver-0.2.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82efc6ae00ba82011f59fa1c22e0b57408ffad9083bac9e71eb2d5e90cceedf2
|
|
| MD5 |
ef69f6e376ff931fff6c47883f048320
|
|
| BLAKE2b-256 |
e8b5fb98618fe2483028342ed04332c970f2f8b0fe8fb035f9437c842886d1ce
|
Provenance
The following attestation bundles were made for sm_resolver-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Sharathvc23/sm-resolver
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sm_resolver-0.2.0-py3-none-any.whl -
Subject digest:
82efc6ae00ba82011f59fa1c22e0b57408ffad9083bac9e71eb2d5e90cceedf2 - Sigstore transparency entry: 2075175851
- Sigstore integration time:
-
Permalink:
Sharathvc23/sm-resolver@2c70afdb0fa4e11384fe0c46281fb66ff5a94148 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Sharathvc23
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2c70afdb0fa4e11384fe0c46281fb66ff5a94148 -
Trigger Event:
push
-
Statement type: