Skip to content

v0.43.1 — Precondition parity and a guard that was not guarding

Choose a tag to compare

@flyersworder flyersworder released this 18 Aug 09:19
· 10 commits to main since this release
c96cbb1

A follow-up to v0.43.0, closing the three issues its own review raised. No new features; no change to anything a correctly-formed contract does.

Fixed

attribute_change validates operator and arity, like the sibling it mirrors (#72)

It is public, accepts an arbitrary MetricDefinition, and its own comments state it cannot assume the object came through validate_decompositions at load — but it re-validated only the convention. reconcile_decomposition validates operator and arity up front for exactly that reason, before running a single query.

Malformed decompositions therefore surfaced as interpreter errors from inside the arithmetic rather than the ValueError the module commits to:

Input Before After
ratio, 1 operand IndexError: list index out of range ValueError: operator 'ratio' requires exactly 2 operands, got 1
ratio / difference, 3 operands too many values to unpack (expected 2, got 3) ValueError: ... requires exactly 2 operands, got 3
product, 0 operands ZeroDivisionError: division by zero ValueError: ... requires at least 2 operands, got 0

The IndexError came from the zero-denominator guard indexing operands[1], so the new block sits before it. Unreachable for any contract-loaded metric — the loader rejects all four shapes — but the asymmetry between two kernels with the same stated precondition posture is the kind that gets copied into a third.

Internal

The pinned round-trip digest now covers metric serialization (#73)

_PINNED_ROUNDTRIP_DIGEST is the repo's broadest guard against canonical-bytes drift — one changed byte anywhere fails it, with no test author needing to have anticipated the field. It was computed over a semantic source with no metrics: key at all, so _dump_metric was never called.

That is worse than a missing test, because the assertion passed either way. Every optional field added to _dump_metric since the pin was written could have been made unconditional — moving the digest of every real contract and invalidating every published ARD attestation — with the test still green. Verified by mutation: removing the if m.decompositions: guard now fails the pin, and before this change that mutation was unreachable.

Convention-default helpers moved to semantic/base.py (#74)

OssieSource was importing two private functions across from yaml_source.py, while every other shared semantic helper lives in base.py. Both operate purely on MetricDefinition / Decomposition, and both had to import VALID_CONVENTIONS and _CROSS_TERM_OPERATORS back out of base.py — constants that had no other consumer in yaml_source.py. After the move that file needs neither.

Upgrading

Nothing to do. No public API changed, and a contract that loads today produces the same canonical bytes and the same digest it did on 0.43.0.