Skip to content

Commit 4aa9896

Browse files
Pigbibicodex
andcommitted
fix: enforce vnext identity uniqueness and ordering
Co-Authored-By: Codex <noreply@openai.com>
1 parent 1b954cf commit 4aa9896

2 files changed

Lines changed: 7 additions & 5 deletions

File tree

src/quant_advisor_research/vnext_identity_schema_v1.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def __post_init__(self) -> None:
117117
if type(self.bindings) is not tuple or not all(isinstance(item, VNextIdentityBinding) for item in self.bindings):
118118
raise _error("index_invalid")
119119
_validate_index(self.bindings)
120+
object.__setattr__(self, "bindings", tuple(sorted(self.bindings, key=_binding_sort_key)))
120121

121122

122123
def _validate_binding(binding: VNextIdentityBinding) -> None:
@@ -169,7 +170,7 @@ def _validate_index(bindings: tuple[VNextIdentityBinding, ...]) -> None:
169170
canonical_periods: set[str] = set()
170171
names: set[str] = set()
171172
artifact_identities: set[tuple[str, str, str, str, str, str, str]] = set()
172-
digest_periods: dict[str, tuple[str, str, str]] = {}
173+
artifact_digests: set[str] = set()
173174
display: dict[str, tuple[bool, set[int]]] = {}
174175
for binding in bindings:
175176
_validate_binding(binding)
@@ -195,11 +196,9 @@ def _validate_index(bindings: tuple[VNextIdentityBinding, ...]) -> None:
195196
if name in names:
196197
raise _error("target_collision")
197198
names.add(name)
198-
period = (binding.period_key, binding.as_of, binding.cadence)
199-
previous = digest_periods.get(binding.artifact_integrity_digest)
200-
if previous is not None and previous != period:
199+
if binding.artifact_integrity_digest in artifact_digests:
201200
raise _error("artifact_digest_conflict")
202-
digest_periods[binding.artifact_integrity_digest] = period
201+
artifact_digests.add(binding.artifact_integrity_digest)
203202
if {item.period_key for item in bindings} - canonical_periods:
204203
raise _error("canonical_missing")
205204

tests/test_vnext_identity_schema_v1_foundation.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def test_schema_v1_roundtrip_and_input_permutation_are_deterministic():
7171
left = parse_vnext_identity_index(payload(canonical, variant))
7272
right = parse_vnext_identity_index(payload(variant, canonical))
7373
assert left == right
74+
assert hash(left) == hash(right)
7475
assert serialize_vnext_identity_index(left) == serialize_vnext_identity_index(right)
7576
assert parse_vnext_identity_index(serialize_vnext_identity_index(left)) == left
7677

@@ -132,6 +133,8 @@ def test_index_requires_one_canonical_and_enforces_display_policy():
132133
def test_full_artifact_identity_and_target_collisions_fail_closed():
133134
with pytest.raises(VNextIdentityError, match="identity_duplicate"):
134135
VNextIdentityIndex((binding(), binding(md=True)))
136+
with pytest.raises(VNextIdentityError, match="artifact_digest_conflict"):
137+
VNextIdentityIndex((binding(), binding(cls=V3_VARIANT, semantic=B, order=1)))
135138

136139

137140
def test_display_order_is_safe_json_integer_and_not_bool():

0 commit comments

Comments
 (0)