diff --git a/arborist/attest/chain.py b/arborist/attest/chain.py index e653a43..cd7736e 100644 --- a/arborist/attest/chain.py +++ b/arborist/attest/chain.py @@ -154,7 +154,7 @@ def entity_chain_iter( skew_q10000=int(body["skew_q10000"]), error_taxonomy={k: int(v) for k, v in body.get("error_taxonomy", {}).items()}, stress_params={k: int(v) for k, v in body.get("stress_params", {}).items()}, - cites=tuple(body.get("cites", ())), + cites=tuple(body["cites"]), schema_version=body["schema_version"], ) yield n, event_hash, fp diff --git a/arborist/attest/fingerprint.py b/arborist/attest/fingerprint.py index 65f0e87..e6df796 100644 --- a/arborist/attest/fingerprint.py +++ b/arborist/attest/fingerprint.py @@ -1,6 +1,6 @@ """Fingerprint schema + canonical encoder + Merkle leaf hash. -Schema version: ``fingerprint-v2``. +Schema version: ``fingerprint-v1``. All numeric fields are integers. Latency lives in microseconds. Skew is quantized to a 4-decimal-place integer (``skew_q10000``). @@ -14,15 +14,13 @@ by ±1 in the last digit of ``skew_q10000`` or one of the std/mean fields. Acceptable for v1; a future revision can compute moments in integer arithmetic if required. -**v2 delta: optional ``cites`` field.** A fingerprint may declare a -``cites`` tuple of other entities' chain hashes that it vouches for -or depends on. The citation is a *claim of dependency*, not a -verification — at commit time arborist does not re-verify the cited -chain. The point is that if a cited chain hash is later falsified -(KS / Mahalanobis drift, manual quarantine, etc.), the audit graph -yields a deterministic blast radius via ``chain.blast_radius()``. -Default empty tuple → equivalent to v1 semantics. Legacy v1 bodies -in storage (no ``cites`` key) are reconstructed with ``cites=()``. +**``cites`` field.** A fingerprint may declare a ``cites`` tuple of +other entities' chain hashes that it vouches for or depends on. The +citation is a *claim of dependency*, not a verification — at commit +time arborist does not re-verify the cited chain. The point is that +if a cited chain hash is later falsified (KS / Mahalanobis drift, +manual quarantine, etc.), the audit graph yields a deterministic +blast radius via ``chain.blast_radius()``. Default empty tuple. """ from __future__ import annotations @@ -35,7 +33,7 @@ from typing import Mapping, Sequence from arborist.merkle import hash_leaf -SCHEMA_VERSION = "fingerprint-v2" +SCHEMA_VERSION = "fingerprint-v1" @dataclass(frozen=True) diff --git a/tests/test_attest_chain.py b/tests/test_attest_chain.py index a226aac..ea1becc 100644 --- a/tests/test_attest_chain.py +++ b/tests/test_attest_chain.py @@ -240,32 +240,3 @@ def test_blast_radius_misses_unrelated_chain_hash(tmp_path): ) assert blast_radius(conn, "f" * 64) == [] - -def test_legacy_v1_body_without_cites_reconstructs_with_empty_tuple(tmp_path): - """v1 events in storage (no `cites` key) must round-trip through entity_chain_iter.""" - from arborist.store import append_audit - - conn = connect(tmp_path / "t.db") - legacy_body = { - "entity_id": "alice", - "session_id": "s-legacy", - "timestamp_utc": "2026-06-05T15:30:00Z", - "domain": "python-debug", - "host_hash": "0" * 64, - "count": 3, - "mean_us": 1000, - "std_us": 100, - "p50_us": 1000, - "p90_us": 1100, - "p99_us": 1200, - "skew_q10000": 0, - "error_taxonomy": {}, - "stress_params": {}, - "schema_version": "fingerprint-v1", - } - append_audit(conn, event_type=EVENT_TYPE, body=legacy_body, subject_root="alice") - seen = list(entity_chain_iter(conn, "alice")) - assert len(seen) == 1 - fp = seen[0][2] - assert fp.cites == () - assert fp.schema_version == "fingerprint-v1"