Skip to content

Commit 6ec56f3

Browse files
committed
feat(v2): port evidentiary type safety primitives — 2.4.0
Ports the four primitives that shipped in TypeScript SDK 2.6.0-alpha.0 (commit 3171480 in agent-passport-system): - claim_evidence_types: ClaimType / RecordType registry, EvidenceProfiles, required_evidence_for - claim_verifier: verify_evidence_claim with forbidden-substitution detection, profile_not_populated honesty, optional open_contestation_resolver hook - downstream_taint: compute_downstream_taint cascade for upheld / remedied contestations, GroundsClass enum - ContestabilityReceipt: minimal Python dataclass carrying the fields the cascade reads (action_id, receipt_id, controller_response.status, optional grounds_class). Wave 1 accountability port deferred; the dataclass widens when Wave 1 ports. Discriminated union design call: tagged-union via dataclass with status: Literal[...] field, no Pydantic dependency added. The to_dict() helper drops None-valued fields so canonical JSON matches what TS produces. Documented in claim_verifier.py module docstring. Cross-impl byte-parity verified against TS test fixtures: tests/v2/fixtures/evidentiary-type-safety/fixtures.json 9 verifier scenarios + 6 cascade scenarios All Python canonical JSON + sha256 match TS exactly. Tests: 398 passed (was 348), 1 skipped, 6 xfailed. +50 new tests. Version 2.3.0 -> 2.4.0 in pyproject.toml and __init__.py. PyPI publish deferred to Tima Touch ID.
1 parent 3bfeb35 commit 6ec56f3

11 files changed

Lines changed: 1919 additions & 6 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
pip install agent-passport-system
1111
```
1212

13-
> **Current PyPI version**: `2.3.0` (default install). Cross-language parity with `agent-passport-system` npm v2.6.0-alpha.0 covers identity, delegation, governance, data source registration, training attribution, per-period attribution settlement, and mutual authentication. Wave 1 accountability and the evidentiary type safety primitives are TypeScript-only this iteration.
13+
> **Current PyPI version**: `2.4.0` (default install). Cross-language parity with `agent-passport-system` npm v2.6.0-alpha.0 covers identity, delegation, governance, data source registration, training attribution, per-period attribution settlement, mutual authentication, and the evidentiary type safety primitives (claim/evidence registry, claim verifier, contestation cascade). Wave 1 accountability primitives are still TypeScript-only this iteration.
1414
1515

1616
## Quick Start
@@ -110,7 +110,7 @@ This Python SDK implements all 8 Agent Passport Protocol layers:
110110
7. **Integration Wiring** — Cross-layer bridges (commerce+intent, coordination+agora)
111111
8. **Agentic Commerce** — 4-gate checkout, human approval, spend limits
112112

113-
Strict subset of the [TypeScript SDK](https://www.npmjs.com/package/agent-passport-system) at npm v2.6.0-alpha.0. Wave 1 accountability primitives (ActionReceipt, AuthorityBoundaryReceipt, CustodyReceipt, ContestabilityReceipt, APSBundle) and the evidentiary type safety primitives (claim-evidence registry, claim verifier, downstream-taint cascade) ship in the TypeScript SDK only this iteration; Python port deferred. Cross-language signature verification continues to work for the primitives Python does ship. Also available via the [MCP server](https://mcp.aeoess.com/sse).
113+
Strict subset of the [TypeScript SDK](https://www.npmjs.com/package/agent-passport-system) at npm v2.6.0-alpha.0. The four evidentiary type safety primitives (claim/evidence registry, claim verifier with forbidden-substitution detection, contestation cascade, GroundsClass extension) ship in `agent_passport.v2` from Python SDK 2.4.0 onward, with cross-impl byte-parity verified against TS-generated fixtures. Wave 1 accountability primitives (ActionReceipt, AuthorityBoundaryReceipt, CustodyReceipt, ContestabilityReceipt, APSBundle) ship in the TypeScript SDK only this iteration; full Python port deferred. The cascade primitive uses a minimal Python ContestabilityReceipt that widens when Wave 1 ports. Cross-language signature verification continues to work for the primitives Python does ship. Also available via the [MCP server](https://mcp.aeoess.com/sse).
114114

115115
## Links
116116

@@ -135,8 +135,10 @@ Strict subset of the [TypeScript SDK](https://www.npmjs.com/package/agent-passpo
135135
```bash
136136
pip install pynacl pytest
137137
PYTHONPATH=src pytest tests/ -v
138-
# 348 passed, 1 skipped, 6 xfailed across 27 test files. Coverage tracks the
138+
# 398 passed, 1 skipped, 6 xfailed across 30 test files. Coverage tracks the
139139
# protocol layers the Python port has actually shipped (see "Strict subset" note above).
140+
# The new test_claim_*, test_downstream_taint files include 15 cross-impl byte-parity
141+
# checks against TS-generated canonical JSON fixtures.
140142
```
141143

142144
## License

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "agent-passport-system"
7-
version = "2.3.0"
8-
description = "Python SDK for the Agent Passport System. Identity, delegation, governance, data source registration, training attribution, per-period attribution settlement, mutual authentication. Cross-language parity with agent-passport-system npm v2.6.0-alpha.0 (Wave 1 accountability primitives in JS SDK; Python port deferred). Product intelligence lives in the private gateway."
7+
version = "2.4.0"
8+
description = "Python SDK for the Agent Passport System. Identity, delegation, governance, data source registration, training attribution, per-period attribution settlement, mutual authentication, evidentiary type safety (claim/evidence registry, claim verifier with forbidden-substitution detection, contestation cascade). Cross-language parity with agent-passport-system npm v2.6.0-alpha.0 verified by byte-identical canonical JSON fixtures. Wave 1 accountability primitives in JS SDK; Python port deferred. Product intelligence lives in the private gateway."
99
readme = "README.md"
1010
license = "Apache-2.0"
1111
requires-python = ">=3.9"

src/agent_passport/__init__.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
Docs: https://aeoess.com/llms-full.txt
2929
"""
3030

31-
__version__ = "2.3.0"
31+
__version__ = "2.4.0"
3232

3333
# Crypto
3434
from .crypto import generate_key_pair, sign, verify, public_key_from_private
@@ -283,3 +283,36 @@
283283

284284
# Canonical JCS (RFC 8785 strict) for modules requiring cross-language signature interop
285285
from .canonical import canonicalize_jcs
286+
287+
288+
# Evidentiary Type Safety primitives (SDK v2.4.0)
289+
# Ports of the four TypeScript SDK 2.6.0-alpha.0 primitives:
290+
# claim-evidence-types, claim-verifier, downstream-taint, GroundsClass.
291+
# ContestabilityReceipt fully ports when Wave 1 accountability ports;
292+
# the cascade-consumed shape ships now as a minimal dataclass.
293+
from .v2 import (
294+
# claim_evidence_types
295+
ClaimType,
296+
RecordType,
297+
EvidenceProfile,
298+
EvidenceProfiles,
299+
required_evidence_for,
300+
# claim_verifier
301+
ClaimVerificationInput,
302+
ClaimVerificationResult,
303+
ClaimVerificationStatus,
304+
EvidenceEntry,
305+
OpenContestationLookup,
306+
OpenContestationResolver,
307+
verify_evidence_claim,
308+
# downstream_taint
309+
ContestStatus,
310+
ContestabilityControllerResponse,
311+
ContestabilityReceipt,
312+
GroundsClass,
313+
TaintCandidate,
314+
TaintedRecord,
315+
TaintedSet,
316+
compute_downstream_taint,
317+
is_contestation_tainting,
318+
)

src/agent_passport/v2/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,33 @@
44
Cross-language signature compatibility: any signed artifact produced by
55
the TS SDK (canonical JSON + Ed25519) verifies in Python and vice versa.
66
"""
7+
8+
# Evidentiary Type Safety primitives (Modules 1, 2, 4).
9+
# Mirrors agent-passport-system 2.6.0-alpha.0.
10+
from .claim_evidence_types import (
11+
ClaimType,
12+
RecordType,
13+
EvidenceProfile,
14+
EvidenceProfiles,
15+
required_evidence_for,
16+
)
17+
from .claim_verifier import (
18+
ClaimVerificationInput,
19+
ClaimVerificationResult,
20+
ClaimVerificationStatus,
21+
EvidenceEntry,
22+
OpenContestationLookup,
23+
OpenContestationResolver,
24+
verify_evidence_claim,
25+
)
26+
from .downstream_taint import (
27+
ContestStatus,
28+
ContestabilityControllerResponse,
29+
ContestabilityReceipt,
30+
GroundsClass,
31+
TaintCandidate,
32+
TaintedRecord,
33+
TaintedSet,
34+
compute_downstream_taint,
35+
is_contestation_tainting,
36+
)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2+
"""Claim → Evidence types (Module 1 of the Evidentiary Type Safety set).
3+
4+
Mirrors src/v2/claim-evidence-types.ts in the TypeScript SDK at
5+
agent-passport-system 2.6.0-alpha.0.
6+
7+
Names a closed set of claims an APS receipt can substantiate, the
8+
record types the protocol can produce, and the mapping between
9+
them. Receipts substantiate specific claims; not every receipt
10+
can substitute for another. This module is the static surface of
11+
that mapping. Verification logic lives in claim_verifier.
12+
13+
Enum string values are byte-identical to the TypeScript SDK so
14+
any serialized form (registry export, audit-log entry, fixture
15+
JSON) interops across implementations.
16+
"""
17+
18+
from dataclasses import dataclass, field
19+
from enum import Enum
20+
from typing import Dict, List, Optional
21+
22+
23+
class ClaimType(str, Enum):
24+
"""Closed taxonomy of claims an evidence bundle can substantiate."""
25+
26+
IDENTITY_VERIFIED = "IDENTITY_VERIFIED"
27+
AUTHORITY_TO_EXECUTE = "AUTHORITY_TO_EXECUTE"
28+
ACTION_EXECUTED = "ACTION_EXECUTED"
29+
BINDING_COMMITMENT = "BINDING_COMMITMENT"
30+
EFFECT_SAFETY_ATTESTED = "EFFECT_SAFETY_ATTESTED"
31+
DERIVATION_TRACED = "DERIVATION_TRACED"
32+
CLAIM_CONTESTED = "CLAIM_CONTESTED"
33+
CLAIM_RESOLVED = "CLAIM_RESOLVED"
34+
BATCH_ATTESTED = "BATCH_ATTESTED"
35+
EVIDENCE_CUSTODY_HELD = "EVIDENCE_CUSTODY_HELD"
36+
37+
38+
class RecordType(str, Enum):
39+
"""Mirrors the existing record-producing primitives the SDK ships.
40+
41+
Names match the TypeScript types in `src/index.ts`. Wave 1
42+
accountability primitives (ActionReceipt, AuthorityBoundaryReceipt,
43+
CustodyReceipt, ContestabilityReceipt, APSBundle) are referenced
44+
here even though their full Python ports haven't shipped yet — the
45+
registry vocabulary is independent of which receipts the Python
46+
SDK can construct.
47+
"""
48+
49+
ActionReceipt = "ActionReceipt"
50+
AuthorityBoundaryReceipt = "AuthorityBoundaryReceipt"
51+
CustodyReceipt = "CustodyReceipt"
52+
ContestabilityReceipt = "ContestabilityReceipt"
53+
APSBundle = "APSBundle"
54+
AccessReceipt = "AccessReceipt"
55+
DerivationReceipt = "DerivationReceipt"
56+
DecisionReceipt = "DecisionReceipt"
57+
ProvisionalStatement = "ProvisionalStatement"
58+
PromotionEvent = "PromotionEvent"
59+
Withdrawal = "Withdrawal"
60+
InstructionProvenanceReceipt = "InstructionProvenanceReceipt"
61+
CognitiveAttestation = "CognitiveAttestation"
62+
63+
64+
@dataclass(frozen=True)
65+
class EvidenceProfile:
66+
"""Static schema for what evidence a given ClaimType requires.
67+
68+
`forbidden_substitutions` maps a RecordType to a human-readable
69+
rationale string. The rationale text is byte-identical across
70+
implementations so audit logs and paper appendices reference the
71+
same canonical wording.
72+
"""
73+
74+
required: List[RecordType]
75+
forbidden_substitutions: Dict[RecordType, str] = field(default_factory=dict)
76+
optional: Optional[List[RecordType]] = None
77+
78+
79+
EvidenceProfiles: Dict[ClaimType, EvidenceProfile] = {
80+
ClaimType.AUTHORITY_TO_EXECUTE: EvidenceProfile(
81+
required=[RecordType.AuthorityBoundaryReceipt],
82+
optional=[RecordType.DecisionReceipt],
83+
forbidden_substitutions={
84+
RecordType.ActionReceipt: (
85+
"Action receipts prove execution, not authority. The boundary "
86+
"ruling is a separate signer (the gateway/evaluator), and "
87+
"conflating them collapses the trust split that makes the "
88+
"audit chain meaningful."
89+
),
90+
},
91+
),
92+
93+
ClaimType.BINDING_COMMITMENT: EvidenceProfile(
94+
required=[RecordType.PromotionEvent, RecordType.ProvisionalStatement],
95+
optional=[RecordType.DecisionReceipt],
96+
forbidden_substitutions={
97+
RecordType.ActionReceipt: (
98+
"Action receipts prove execution or communication, not "
99+
"binding commitment."
100+
),
101+
},
102+
),
103+
104+
# TODO: populate required/optional records and forbidden_substitutions.
105+
ClaimType.IDENTITY_VERIFIED: EvidenceProfile(required=[]),
106+
107+
# TODO: populate required/optional records and forbidden_substitutions.
108+
ClaimType.ACTION_EXECUTED: EvidenceProfile(required=[]),
109+
110+
# TODO: populate required/optional records and forbidden_substitutions.
111+
ClaimType.EFFECT_SAFETY_ATTESTED: EvidenceProfile(required=[]),
112+
113+
# TODO: populate required/optional records and forbidden_substitutions.
114+
ClaimType.DERIVATION_TRACED: EvidenceProfile(required=[]),
115+
116+
# TODO: populate required/optional records and forbidden_substitutions.
117+
ClaimType.CLAIM_CONTESTED: EvidenceProfile(required=[]),
118+
119+
# TODO: populate required/optional records and forbidden_substitutions.
120+
ClaimType.CLAIM_RESOLVED: EvidenceProfile(required=[]),
121+
122+
ClaimType.BATCH_ATTESTED: EvidenceProfile(
123+
required=[RecordType.APSBundle],
124+
),
125+
126+
ClaimType.EVIDENCE_CUSTODY_HELD: EvidenceProfile(
127+
required=[RecordType.CustodyReceipt],
128+
forbidden_substitutions={
129+
RecordType.ActionReceipt: (
130+
"Action receipts prove what was done, not who held the "
131+
"evidence afterward."
132+
),
133+
},
134+
),
135+
}
136+
137+
138+
def required_evidence_for(claim_type: ClaimType) -> EvidenceProfile:
139+
"""Return the EvidenceProfile registered for a claim type."""
140+
return EvidenceProfiles[claim_type]

0 commit comments

Comments
 (0)