Skip to content

v0.18.0 — warm answers were quieter than fresh ones (wire contract 4)

Choose a tag to compare

@datumwise datumwise released this 31 Aug 12:42

columna 0.18.0 · columna-core 0.18.0 · columna-server 0.11.0 · wire contract "3" → "4"

The defect, stated by its consequence

On a fresh store the first asker received less disclosure than the second, for the same question on the same data.

Every value identical, every caveat true. A mechanical fact — "served from cache" — was wearing a semantic name on the semantic channel. Found as OF-24 on 2026-07-27, ruled (a) the same day, landed here.

Two channels

  • disclosures — SEMANTIC. What is true of the answer. Call-invariant, and the sole input to outcome, rollup_severity and materiality.
  • mechanical — OBSERVATIONAL. What happened on this call. Today that is exactly one thing.

Every severity and materiality property reads caveats and never mechanical, so the separation is enforced structurally, not by convention. A standing test marks a mechanical caveat critical and asserts the frame severity stays none.

mechanical is emitted on every column and frame, empty when there is nothing to say, so a consumer never has to distinguish absence-of-facts from absence-of-support.

Two repairs that had to ride with it

Warm TOUCH answers were quieter than fresh ones. The touch path returned from its cache before bridge coverage and the Φ fill dispositions were computed, and CacheEntry had nowhere to put them — so a warm answer silently dropped real semantic facts, one MATERIAL. The cache now carries the semantic disclosure the cold path produced; the key was already pinned to a data version, so the same key at the same version denotes the same data and therefore the same semantic facts. An entry with no stored disclosure falls through and recomputes rather than serving a quieter answer.

A coverage shortfall is MATERIAL. It rode TRANSPORT → provenance → IMMATERIAL, which could not trip disclose on its own, while the correct slot — coverage → denominator_population → MATERIAL — sat wired with no producer. The emitting line's own comment said COVERAGE while the code said TRANSPORT. Fixed on both faces that can have a shortfall; the ASSIGN one was not in the original finding and surfaced only when the probe was re-run after fixing TOUCH. Full coverage stays TRANSPORT — "no shortfall" is a faithful-step record, not a material condition.

They shipped together deliberately. Re-grading coverage while the warm path still dropped it would have made the divergence outcome-visible — disclose cold, serve warm — strictly worse than the quiet version it replaced.

Why the contract moved

freshness moved. The same utterance over the same data previously emitted it inside disclosures on a warm call and not on a cold one; it now never appears there. A changed value for an existing field on an unchanged utterance is the canonical break-by-version case — the same reasoning that bumped "1" → "2" for WP-NAME-1. Keeping contract "3" would tell consumers that one contract still describes the same semantics when it no longer does.

For consumers: if you read disclosures to decide what an answer means, nothing changes except that freshness is no longer among them. If you relied on it being there, read mechanical. Outcome and severity are unaffected by the mechanical channel by construction.

Verification

918 passed / 23 skipped. Nine new standing tests, six of which fail against the pre-fix source; the other three are non-discriminating by design and are documented as such rather than counted.