Establishes the canonical Delta-CRDT reference contract so a future native (Rust/Zig) backend is gate-G1-eligible under ADR-0196 — the ZC-0 'contract pinning' slice. No Zig code; ZC-1+ remains gated at G2. - vault/crdt.py: canonical Python reference (ArenaEntry, Delta, LocalArena, merge_kernel, canonical_bytes, delta_hash). Pure content law — content- addressed by IEEE-754 bits then provenance; no normalization, no versor closure, no global Vault writes. - ZC-0 contract tests (semilattice C-1..C-5; content ordering / signed-zero / NaN bit-addressing; C-7 no-global-write) — all failable (mutation-checked: no-dedup breaks C3/C5, arrival-order breaks C1). - Golden fixture corpus (tests/fixtures/crdt/) regenerated deterministically from the reference; single source of truth also emits the Rust expected hex. - core-rs: Delta::canonical_bytes + test_crdt_hash_parity.rs proving Rust produces byte-identical canonical_bytes to the Python reference. - ADR-0180 -> Accepted: locked contract, byte layout, obligation map, and the explicit boundary that no Zig is authorized. Verification: ZC-0 21 passed, Rust arena+parity 16 passed, architectural invariants 40 passed, smoke 67 passed. Serving frozen: 7/8 lane SHAs match; the public_demo miss is a pre-existing wall-clock budget overrun (ADR-0099, ~46-48s > 30s) reproduced identically on clean main — environmental.
157 lines
5.6 KiB
Python
157 lines
5.6 KiB
Python
"""Regenerate tests/fixtures/crdt/merge_fixtures.json from the canonical
|
|
reference (vault/crdt.py). Run: uv run python tests/fixtures/crdt/_generate.py
|
|
|
|
The emitted JSON is the *golden* G1 fixture corpus (ADR-0180 / ADR-0196 gate
|
|
G1). Changing the canonical form in vault/crdt.py changes these hashes; the
|
|
contract tests pin them, so a golden diff is a reviewed, deliberate act.
|
|
|
|
Versor components use only exactly-f32-representable, exactly-JSON-representable
|
|
values (integers, signed zeros, halves) so the corpus round-trips with zero
|
|
float ambiguity. NaN / signed-zero *bit*-distinctness is exercised in-code in
|
|
test_crdt_content_ordering.py, not here.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
sys.path.insert(0, str(_REPO_ROOT))
|
|
|
|
from vault.crdt import ( # noqa: E402 # pyright: ignore[reportMissingImports]
|
|
VERSOR_COMPONENTS,
|
|
ArenaEntry,
|
|
Delta,
|
|
canonical_bytes,
|
|
delta_hash,
|
|
merge_kernel,
|
|
)
|
|
|
|
_OUT = Path(__file__).resolve().parent / "merge_fixtures.json"
|
|
# Rust parity fixture lives under core-rs/tests/fixtures/ (a *subdir* of tests/,
|
|
# so cargo does not compile it as a standalone integration-test target).
|
|
_RUST_OUT = _REPO_ROOT / "core-rs" / "tests" / "fixtures" / "crdt_parity_expected.rs"
|
|
|
|
|
|
def versor(**components: float) -> list[float]:
|
|
"""Build a 32-float versor; unset indices default to 0.0."""
|
|
v = [0.0] * VERSOR_COMPONENTS
|
|
for idx, value in components.items():
|
|
v[int(idx.lstrip("c"))] = value
|
|
return v
|
|
|
|
|
|
def entry(versor_vals: list[float], provenance: bytes) -> ArenaEntry:
|
|
return ArenaEntry.of(versor_vals, provenance)
|
|
|
|
|
|
def encode_entry(e: ArenaEntry) -> dict:
|
|
return {"versor": list(e.versor), "provenance_hex": e.provenance.hex()}
|
|
|
|
|
|
def encode_delta_input(entries: list[ArenaEntry]) -> list[dict]:
|
|
return [encode_entry(e) for e in entries]
|
|
|
|
|
|
def build_cases() -> list[dict]:
|
|
cases: list[dict] = []
|
|
|
|
def add(name: str, description: str, delta_entry_lists: list[list[ArenaEntry]]):
|
|
deltas = [Delta.from_entries(es) for es in delta_entry_lists]
|
|
merged = merge_kernel(deltas)
|
|
cases.append(
|
|
{
|
|
"name": name,
|
|
"description": description,
|
|
"input_deltas": [encode_delta_input(es) for es in delta_entry_lists],
|
|
"expected_merged_entries": [encode_entry(e) for e in merged.entries],
|
|
"expected_canonical_bytes_hex": canonical_bytes(merged).hex(),
|
|
"expected_delta_hash": delta_hash(merged),
|
|
}
|
|
)
|
|
|
|
# 1 — single entry, single delta.
|
|
add(
|
|
"single_entry",
|
|
"One delta, one entry: identity of the canonical form.",
|
|
[[entry(versor(c0=1.0), b"a")]],
|
|
)
|
|
|
|
# 2 — duplicate byte-identical entries collapse (idempotence at entry level).
|
|
add(
|
|
"dedup_within_delta",
|
|
"Byte-identical entries collapse to one (join idempotence).",
|
|
[[entry(versor(c0=1.0), b"a"), entry(versor(c0=1.0), b"a")]],
|
|
)
|
|
|
|
# 3 — same versor, distinct provenance: BOTH retained (no over-dedup).
|
|
add(
|
|
"distinct_provenance_retained",
|
|
"Same versor under different provenance are distinct content.",
|
|
[[entry(versor(c5=2.0), b"left"), entry(versor(c5=2.0), b"right")]],
|
|
)
|
|
|
|
# 4 — three deltas merged; corpus pins the canonical (content-sorted) result.
|
|
add(
|
|
"three_delta_merge",
|
|
"Union of three deltas, content-sorted and deduped.",
|
|
[
|
|
[entry(versor(c1=3.0), b"z"), entry(versor(c0=1.0), b"a")],
|
|
[entry(versor(c2=2.0), b"m"), entry(versor(c0=1.0), b"a")],
|
|
[entry(versor(c10=5.0), b"q")],
|
|
],
|
|
)
|
|
|
|
# 5 — signed-zero is distinct content (+0.0 bits != -0.0 bits).
|
|
add(
|
|
"signed_zero_distinct",
|
|
"+0.0 and -0.0 have distinct bit patterns -> distinct entries.",
|
|
[[entry(versor(c0=0.0), b"p"), entry(versor(c0=-0.0), b"p")]],
|
|
)
|
|
|
|
return cases
|
|
|
|
|
|
def write_rust_fixtures(cases: list[dict]) -> None:
|
|
"""Emit the Rust-side expected canonical_bytes hex so core-rs/tests can
|
|
assert byte-parity without a JSON parser dependency. Python stays the single
|
|
source of truth."""
|
|
lines = [
|
|
"// @generated by tests/fixtures/crdt/_generate.py — DO NOT EDIT.",
|
|
"// Expected canonical_bytes hex per case (mirrors merge_fixtures.json):",
|
|
"// the Python-canonical Delta-CRDT G1 contract (ADR-0180 / ADR-0196)",
|
|
"// the Rust substrate must reproduce byte-for-byte.",
|
|
"// (include!d mid-module, so plain // comments — not //! — are required.)",
|
|
"",
|
|
"pub const EXPECTED_CANONICAL_BYTES_HEX: &[(&str, &str)] = &[",
|
|
]
|
|
for case in cases:
|
|
lines.append(
|
|
f' ("{case["name"]}", "{case["expected_canonical_bytes_hex"]}"),'
|
|
)
|
|
lines.append("];")
|
|
_RUST_OUT.parent.mkdir(parents=True, exist_ok=True)
|
|
_RUST_OUT.write_text("\n".join(lines) + "\n")
|
|
|
|
|
|
def main() -> None:
|
|
corpus = {
|
|
"_comment": (
|
|
"Golden Delta-CRDT fixture corpus (ADR-0180 / ADR-0196 gate G1). "
|
|
"Regenerate with: uv run python tests/fixtures/crdt/_generate.py"
|
|
),
|
|
"versor_components": VERSOR_COMPONENTS,
|
|
"cases": build_cases(),
|
|
}
|
|
_OUT.write_text(json.dumps(corpus, indent=2, sort_keys=False) + "\n")
|
|
write_rust_fixtures(corpus["cases"])
|
|
print(f"wrote {_OUT} ({len(corpus['cases'])} cases)")
|
|
print(f"wrote {_RUST_OUT}")
|
|
for case in corpus["cases"]:
|
|
print(f" {case['name']:30s} hash={case['expected_delta_hash'][:16]}…")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|