Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions specs/001-python-api/assessment-2026-10-12.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@
The wrapper's constitutional risks are dilution risks — a convenience the core forbids, a fallback that fakes the guarantee, an error that hides a verification failure. The claims target exactly those, and each is pinned by a test against the *real* extension (CI builds it from the core repo; nothing is mocked). ITERATE reflects one honest draft (distribution), not a defect in what shipped.

*Process note:* v0.1 was built 2026-10-11 before this repo had a spec-kit scaffold — recorded deviation (Constitution Article IV). This catch-up (2026-10-12) installed the full flow; it governs from Spec 002 onward.

## Addendum — 2026-10-12: float-roundtrip companion (AXIOVEX/axiomere Spec 002 §7)

The core's Spec 002 fixed a reader-side float defect: serde_json's default parser read back decimals needing ≥ 17 significant digits 1 ULP high, so untampered logs carrying such floats failed hash verification on reopen — the failure Tekmerion's platform ledger hit downstream. The §7 companion item for this repo is discharged on branch `companion/float-roundtrip-regression`:

- **Build:** no code, API, or dependency-pin change in this package. The workflow checks out AXIOVEX/axiomere `main` unpinned, and that main now carries the fix at `40d6933` (serde_json `float_roundtrip` enabled in the core and bindings crates). The local verification build pinned its core checkout to `40d6933` explicitly and followed the workflow's maturin route.
- **Regression test:** `tests/test_float_roundtrip.py` (2 tests) appends the exact downstream reproduction `{"ts_like": 1791810119.6349843, "plain": 0.5}` plus a sweep of trigger-class floats (≥ 17-significant-digit plain and exponent forms, smallest/largest finite, smallest subnormal, integral-valued, signed zero), then requires `Database.open`, `verify()`, and `verify_file` all to pass with bit-exact read-back, and the reopened log to stay appendable. Full suite: **7 passed** against the fixed core.
- **Teeth check:** the same two tests were run against a wheel built from the pre-fix core (`43d4cbc`): both fail with the defect's exact signature (`verify()` already false in-session; `hash-mismatch` at seq 1). The regression cannot pass vacuously on an unfixed core.
- **Claim effect:** none re-scored, no claim status changed. The tests stand as observed evidence for the wrapper's verify-on-reopen behavior (sound logs open and verify; APY-001-03 covers the refusal side). Tekmerion-side environments pick the fix up on their next extension rebuild. No release was cut — publishing remains owner-gated.
81 changes: 81 additions & 0 deletions tests/test_float_roundtrip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Regression: float payloads written by this package verify on reopen.

Companion to AXIOVEX/axiomere Spec 002 (float canonicalization). The
core's reader once parsed decimals needing >= 17 significant digits
1 ULP high (serde_json without the ``float_roundtrip`` feature), so an
untampered log written through this wrapper failed hash verification
on reopen — the failure Tekmerion's platform ledger hit downstream
with a high-precision timestamp float. These tests pin the exact
downstream reproduction plus a sweep of the characterized trigger
class against the real extension, so the defect cannot silently
return on this side. On a pre-fix core build, every test here fails:
``Database.open`` raises ``verification failed at seq 1:
hash-mismatch`` and in-session ``verify()`` already reports false.
"""

import struct

import axiomere

REPRODUCTION = {"ts_like": 1791810119.6349843, "plain": 0.5}

# Finite floats whose shortest decimal form needs >= 17 significant
# digits (the trigger class), plus boundary shapes from the core
# spec's characterization matrix.
TRIGGER_FLOATS = [
1791810119.6349843, # the downstream reproduction value
-1791810119.6349843, # negative twin
1.0086398161248257e149, # exponent form, 17 significant digits
0.30000000000000004, # 0.1 + 0.2 — 17 significant digits
123456789.12345679, # 17 significant digits, plain form
2.2250738585072014e-308, # smallest normal
1.7976931348623157e308, # largest finite
5e-324, # smallest subnormal
1.0, # integral-valued float
-0.0, # signed zero
]


def _bits(x: float) -> bytes:
return struct.pack("<d", x)


def test_reproduction_payload_verifies_on_reopen(tmp_path):
p = str(tmp_path / "t.axmr")
db = axiomere.Database.create(p)
db.snapshot("reading", dict(REPRODUCTION), actor="alice")
assert db.verify().ok

reopened = axiomere.Database.open(p) # must not refuse a sound log
v = reopened.verify()
assert v.ok and v.events == 1

ok, count, reason = axiomere.verify_file(p)
assert ok and count == 1 and reason is None

content = reopened.current()["reading"]["content"]
assert _bits(content["ts_like"]) == _bits(REPRODUCTION["ts_like"])
assert _bits(content["plain"]) == _bits(REPRODUCTION["plain"])


def test_trigger_float_sweep_verifies_on_reopen(tmp_path):
p = str(tmp_path / "t.axmr")
payload = {f"v{i}": x for i, x in enumerate(TRIGGER_FLOATS)}
db = axiomere.Database.create(p)
db.snapshot("sweep", payload, actor="alice")
assert db.verify().ok

reopened = axiomere.Database.open(p)
v = reopened.verify()
assert v.ok and v.events == 1

ok, count, reason = axiomere.verify_file(p)
assert ok and count == 1 and reason is None

content = reopened.current()["sweep"]["content"]
for key, original in payload.items():
assert _bits(content[key]) == _bits(original), key

# A repaired-path log stays appendable after reopen.
reopened.snapshot("after", {"ok": True}, actor="alice")
assert reopened.verify().ok
Loading