Releases: views-platform/views-frames
Release list
views-frames 2.0.0
A frame's index must actually be an index.
Until now you could hand a frame anything as its index — a different frame, or a bare object with an n_rows attribute — and it was accepted. The frame was built, it looked fine, and the published conformance suite said it was fine. That now raises TypeError.
If you pass a SpatioTemporalIndex, you will not notice this release. That is what the type hint has always required and what every example does. Change your constraint, re-lock, carry on:
- views-frames = ">=1.10.2,<2"
+ views-frames = ">=2.0.0,<3"This is the package's first MAJOR and the first move of CONFORMANCE_FLOOR since the v1.0.0 freeze (1.0.0 → 2.0.0). Decision, reasoning and full migration: ADR-028.
What actually breaks
| If your code… | Before | Now |
|---|---|---|
passes a SpatioTemporalIndex as index |
works | works, unchanged |
passes a frame or other object as index |
silently constructed | TypeError |
passes a non-index to reindex/reindex_fill |
AttributeError on _level |
TypeError, naming what was expected |
mutates frame.values in place |
silently corrupted buffer-sharing frames | ValueError: read-only |
calls map_estimate on inf/NaN draws |
bare IndexError |
ValueError naming the cause |
Only row 4 can plausibly break working code, and ADR-025 already documented that operation as unsupported.
Why it was worth a MAJOR
Every summarizer reads .values and never .index, so a frame built with a non-index returned numerically correct answers — and assert_summarizer_contract certified it. That checker is published under ADR-016 and consumers run it in their own CI, and docs/CICs/Conformance.md names "a checker that cannot detect a violation" as the failure mode it must never have. A false pass is worse than no checker, because it is evidence you are entitled to rely on.
Found by a falsification audit run against the claim that this package was finished.
Also in this release
frame.valuesis write-protected (register C-66) — as a read-only view, so zero-copy andmmapsurvive and your own array stays writeable.map_estimaterejects non-finite draws (C-57), matchingexceedanceandexpected_shortfall.CONFORMANCE_FLOOR→2.0.0. Your CI will assert a new contract version. First move in the package's history.
Adoption issues are open in views-faoapi, views-postprocessing and views-crafdapi.
Full detail in CHANGELOG.md.
views-frames 1.11.0
The published MAP-containment law was wrong on tied draws, and the governance documents had drifted from the code in ways nothing checked. This release fixes the first, corrects the second, and adds the CI checks that keep both from drifting again.
CONFORMANCE_FLOOR stays 1.0.0, and no public API was added, changed or removed. If you are upgrading from 1.10.2, nothing in your code needs to change.
MINOR rather than PATCH because the conformance suite now asserts a different set of HDI floors than it did in 1.10.2.
The fix that affects consumers
assert_summarizer_contract failed on integer count posteriors (register C-88). The MAP-containment law decided which HDI floors provably contain the tower tip using floor(m·S)+1 — a floor’s index span. The tip is the median of the draws whose value lies inside the floor. Those agree only when draws are distinct: duplicated endpoint values put more draws inside the same bounds, so narrower floors were certified that hold less than half the tip floor, and the law then asserted containment for them.
Measured on zero-inflated Poisson posteriors at S ∈ {32, 64, 128}: 30 of 500 rows failed, every one on the 0.15 floor. After the fix, 0 of 500.
This is published under ADR-016 and consumers run it in their own CI, so the failure landed as another repository going red on correct data — and integer counts are this platform’s primary shape.
If your CI was red on this check with count data, upgrade and it will pass. If it was green, it stays green: the correction narrows what the suite asserts.
Also fixed
The two IO codecs disagreed on a non-JSON metadata value (C-90). io/npz silently stringified it where io/arrow raised — a datetime timestamp reloaded as a string into a field declared int | None. Both now raise, per ADR-008.
New checks
Import contracts, three documentation-completeness assertions, examples/ execution, a cross-version parquet/npz fixture written by v1.8.0 code, and the architecture-tree check.
Governance
ADR-002, ADR-018, GOVERNANCE.md, the physical-architecture standard, README and the CIC set now match the code, with a new FrameMetadata contract. CLAUDE.md gains a maintenance-mode section: this package is finished, and its register’s open entries are a log of accepted conditions rather than a backlog.
Full detail in CHANGELOG.md.
v1.10.2
No behaviour change. This release publishes work on the checks, the tests and the
documentation. The only change under src/ is a corrected docstring. CONFORMANCE_FLOOR
stays 1.0.0, and no public API was added, changed or removed.
If you are upgrading from 1.10.1, nothing in your code needs to change.
Fixed
FeatureFrame.from_2dwas documented as a "deprecated shim". It is not deprecated —
it builds a frame from a 2-D(N, F)array of unsampled features and adds the trailing
sample axis to give(N, F, 1). Since the sample axis is always explicit (ADR-012),
that is the ordinary constructor for deterministic features. The method docstring, the
module docstring, the constructor'sValueErrormessage and the contract file were all
corrected — the first pass missed the last two, so the same file contradicted itself for
a while (register C-76).
Changed — checks
docs/validate_docs.shnow runs in CI. It checks documentation consistency,
including that the README's version banner matchespyproject.toml. That banner check
had been added specifically to stop version drift recurring, but the script was never
wired into CI — it only ran when someone typed it (register C-74, half closed here; the
formatting half follows).
Changed — tests
- Eleven falsification tests now check the code instead of the README's wording. They
were written before the package existed, when asserting that the design document had
decided something was the only check available. They had reached the point of failing
when someone reworded a paragraph and passing when the code broke. Ten were rewritten
against behaviour; one was retired becausetest_import_enforcement.pyalready enforces
it directly (register C-75). - Among them, the check that no legacy
priogrid_gidalias exists now inspects the code
rather than the documentation — a guard against something that was actually attempted.
Changed — documentation
-
New contract document:
docs/CICs/Conformance.md. The published conformance suite —
the checks consumers run in their own CI — had no contract describing what it guarantees,
while the contract index claimed every shipped surface was covered. It now documents each
check, what it deliberately does not verify, and the failure mode that matters most: a
checker must fail when handed a frame that misreports itself (register C-81). -
ADR-027 records the decision to decline issue #113, which asked for a one-line
shortcut for building aPredictionFrame. Construction stays two-step. The design that
had been agreed for it is preserved in the ADR, along with what would justify revisiting
it (register C-52, C-53, C-54). -
The reconciliation production-slice check is closed: the comparison tool and the runbook
requirement for it both shipped some time ago (register C-58). -
Documentation now cites code by name rather than by line number
(_validation.py::coerce_values, not_validation.py:64). Line numbers had already
drifted unnoticed in two entries.
v1.10.1
[1.10.1] — 2026-07-28
io.arrow.load now validates the wire-contract row order before reshaping (#199 item 1).
Fail-loud hardening of the parquet load path; no API change; CONFORMANCE_FLOOR stays 1.0.0.
Fixed
- Silent sample-slot corruption on out-of-order parquet input.
arrow.savewrites the
row order as the contract (sample = tile(arange(S), N);io/arrow.py) butload
reconstructed positionally without ever checking it — a reordered, truncated, or
foreign-rewritten table reshaped plausible floats into the wrong sample slots with no
error.loadnow validates the layout first and raisesValueErroron: a row count
that is not a positive multiple of the header'sn_samples(truncated/filtered table);
asamplecolumn deviating from the written tile order (row-level reorder); or
time/unitnot constant within a sample block (rows swapped between cells — the case
the tile check alone cannot see). A whole-cell block reorder (identifiers travel with
their draws) remains a consistent table and still loads. Implements the check
views-postprocessing ADR-013 §4.5(b) previously required every consumer to run
themselves on a separate raw-table read — the leaf now hardens all consumers at once.
Register C-72. (#199 item 2 — mmap/partitioned arrow reading — remains open.)
v1.10.0
[1.10.0] — 2026-07-27
The dense-grid fill primitive (ADR-026) — unblocks pandas-free FAO ingestion (#203).
Additive MINOR; CONFORMANCE_FLOOR stays 1.0.0.
Added
frame.reindex_fill(other, *, fill_value)on all three sibling frames
(PredictionFrame/FeatureFrame/TargetFrame, WET per ADR-011): align toother's
rows with no superset requirement — present rows pass through bit-exact, absent
rows get the caller'sfill_valuebroadcast across the trailing axes (NaNlegal;
keyword-only and required — no silent default, ADR-009). The result's index is
other; metadata (andfeature_names) preserved. Owns the-1-sentinel scatter once:
a consumer hand-rollingvalues[pos]silently picks the last row for absent cells.
Inherits the C-21 row-uniqueness stance (unique rows assumed in self; duplicate
target rows allowed and repeat).SpatioTemporalIndex.cartesian(times, units, level): the dense product-index
constructor — every(time, unit)combination in canonical time-major order, from
explicit arrays only (deriving them, e.g. "units of the last time step", is
consumer policy). Fails loud on duplicated input values (a duplicated product input
manufactures duplicate rows → undefined same-level joins, C-21).assert_reindex_fill_lawin the published conformance suite (ADR-016): result
index equals the target row-for-row; present rows bit-exact; absent rows equal the
fill (NaN-safe); on a superset frame the fill degenerates toreindex.- Consumer note: faoapi's
dense_grid.pycan now delegate (its last-step-entity rule +
C-87 dropped-entity check stay consumer-side) and drop its pandas implementation
(faoapi #242). Densification allocates the full dense buffer — a deliberate, costly
act at grid scale (documented on both symbols).
v1.9.0
[1.9.0] — 2026-07-24
The tower-tip MAP reads the top floor (ADR-019 Amendment 3): tip_mass 0.5 → 0.25.
Behavior change to tower_point/summarize_tower outputs, shipped MINOR per the C-44/C-45
precedent (estimator amendment with ADR evidence); CONFORMANCE_FLOOR stays 1.0.0.
Changed
tip_massdefault 0.5 → 0.25 (the top-quartile floor). The tower-tip MAP is now the
median of the top floor of the published tower, matching the design intent the name
always promised. Evidence (research/map_hdi/tip_mass_study.py, 1000-replicate battery +
duplicate-capture frontier + the real-cell C-44 gate): the 0.5 shorth carried a structural
rightward bias that does not shrink with sample count; 0.25 roughly halves it, beats the
shorth on RMSE at pooled S, reads zero-inflated cells exactly, and passes the real-cell
zero-stack gate with margin (masses ≤ 0.15 resurrect the C-44 signal loss; 0.20 has zero
margin). Consumer note: published MAPs shift toward the mode on skewed cells — the intended
C-32 direction.
Added
- MAP-containment law in
assert_summarizer_contract: every floor holding more than half
the tip floor's draws (asymptotically mass >tip_mass/2 = 12.5%) provably contains the
tip — wider floors by nesting, narrower qualifying floors by the sub-window trim argument.
All published bands (50/90/95/99) qualify; the unguaranteed region shrinks versus the old
default (was: everything below 0.5). research/map_hdi/tip_mass_study.py— the committed evidence trail for the amendment.research/figures/— permanent, seeded generators for the PRN06 tower figures (overlay +
detail, upright-tower rendering), withreports/plots/as the gitignored output home.
v1.8.1
[1.8.1] — 2026-07-02
Falsification-audit hardening (four-axis audit 2026-07-02; register C-67/C-68/C-69/C-70).
Bug fixes with an identical contract — the code now honors what the docs already promised.
No public-surface change; CONFORMANCE_FLOOR stays 1.0.0.
Fixed
reconcile_proportionalconserves exactly for any nonzero draw sum (C-68, the audit's one
hard finding). The torch-port's+ 1e-8denominator epsilon — a float32 no-op for draw sums
≳ 0.1 but a silent deflator for tiny nonzero sums (a draw sum of 1e-8 reconciled a country
total of 100 to 50, with no error signal, violating the Reconcile.md §3 sum-to-country
guarantee) — is replaced by an explicit all-zero-draw guard: exact division for any nonzero
sum; all-zero draws stay zero exactly as before. Bit-identical on all realistic data
(torch-oracle parity unchanged and green).- Negative country totals now fail loud (C-68/F8):
reconcile_proportionalraises
ValueErrorinstead of silently clamping the output to zero (sum 0 ≠ the requested total). - The published conformance suites refuse to run under
python -O(C-67): all three
(views_frames.conformance, summarize, reconcile) now guard their entry points with
_require_assertions()— under optimized bytecode (which strips the suites'assert
statements) they raiseRuntimeErrorinstead of silently reporting green. - Empty-index
searchsortedreturns all-1(C-69) — the documented not-found value —
instead of crashing with an obscureIndexError(thenp.clip(pos, 0, -1)corner).
Notes
- Regression pins for all four fixes:
tests/test_falsification_safety_audit_2026_07.py. Reconcile.md§6 documents the two reconcile behaviors;proportional.py's module docstring
records the deliberate (bit-parity-preserving) deviation from the torch original.- Register: C-67/C-68/C-69 registered-and-resolved; C-70 (the audit's docs/tests polish
bundle) opened and cleared in the same release (below).
Tests
- C-70 test adds (#195): the share-proportionality law (the method's defining
forecast-proportion property, previously pinned only by the frozen-oracle fixtures); an mmap
read-only pin (writeable is False, in-place write raises); the reconcile
missing-(time, priogrid_gid)-mapping-entry raise.
Documentation
- C-70 docs refresh (#196):
CLAUDE.mdrewritten for the released three-package reality;
README banner → v1.8.0 + chronicle; ADR-013 as-built amendment (feature_names); CIC accuracy
fixes (§5 artifact names →values.npy, PredictionFrame §6 real dtype behavior, index §6
NaN-via-dtype, Reconcile §10 pinning files); CICs/ADRs README framing refreshed; and a
recurrence guard —validate_docs.shnow checks the README banner's MAJOR.MINOR against
pyproject.toml.
v1.8.0
[1.8.0] — 2026-06-28
Native point-country broadcast in views_frames_reconcile (ADR-023 amendment, #143 / Epic #142),
the three showcase notebooks (Epic #166), and a governance/test hardening pass (Epic #179). All
additive — the frozen leaf and summarize public surface are unchanged, and the hardening work makes
no src/ behaviour change; CONFORMANCE_FLOOR stays 1.0.0.
Added
ReconciliationModule.reconcileaccepts a point country (cm.sample_count == 1) against a draws
grid (pgm.sample_count == S): the point is broadcast across theSdraws inside the orchestrator
(np.tile), so callers no longer tile it themselves (the DRY home of pipeline-core's WET
align_country_to_grid, #143). The aligned-draws path (cm.sample_count == S) is byte-for-byte
unchanged; any other count still fails loud.ReconciliationModule.reconcile_result(cm, pgm) -> ReconciliationResult(#144) — reconciles and
reports the mode (POINT_BROADCAST|ALIGNED_DRAWS) + method (proportional) on a returned
ReconciliationResult. The mode is returned, never stamped on the leaf's genericFrameMetadata
(ADR-020 / register C-47 — the numpy leaf carries no reconciliation vocabulary).reconcileis
unchanged (it returnsreconcile_result(...).frame). New public names:ReconciliationResult,
POINT_BROADCAST,ALIGNED_DRAWS,METHOD_PROPORTIONAL.
Notes
- The broadcast lives entirely in
views_frames_reconcile/module.py; the leafproportionaland the
parity-frozengroupinghot loop are untouched, so the torch-oracle parity is exact (0.000e+00). - The aligned-draws mode remains the documented per-draw approximation. ADR-024 (#145) records the
design direction + deferral for the principled joint upgrade (and correctsproportional.py's
ambiguous "C-37" reference; register C-62). Design-only — no code.
Documentation
- Three showcase notebooks (
notebooks/01_frames,02_summaries,03_reconciliation; Epic #166):
public-frozen-API-only, synthetic-data teaching notebooks for the frames contract, the posterior
summaries (with a calibration/coverage + PIT panel), and reconciliation — including a
bit-identity-≠-method-quality panel and a toy-lattice spatial view (register C-59/C-60/C-61). docs/CICs/Reconcile.md(Epic #179) — the package-level Class Intent Contract for
views_frames_reconcile(§1–§11): the sum-to-country / zero-preservation / non-negativity /
de-mutation guarantees, the point/aligned mode contract, the five fail-loud validation guards- the per-draw-approximation caveat, and the green/beige/red test alignment. The reconcile package
was the last non-trivial surface without a CIC (ADR-006); register C-64 resolved.
- the per-draw-approximation caveat, and the green/beige/red test alignment. The reconcile package
- ADR-025 — value-buffer immutability is by convention; only the index is enforced (Epic #179).
Corrects the "immutable value objects" contract (the three frame CICs §9/§3 + README design
principle 2) to match the code: the index (time/unit) issetflags(write=False)-enforced; the
value buffer is immutable by convention (left writeable to preserve zero-copy /mmap— mutating
.valuesin place is unsupported). Thesetflags-enforce on.valueswould be a MAJOR
("tightening an invariant" on a frozen-surface member, GOVERNANCE/ADR-018), so it is recorded as a
deferred MAJOR-rider, not done now; register C-63 resolved (contract corrected).
Tests
- Adversarial (red) test hardening (Epic #179), no
src/change, 100% line+branch coverage held:- the non-finite (NaN / ±inf) fail-loud guard in
exceedance/expected_shortfallis now pinned on
the blocked (multi-block) path — the bad draw placed in a non-first block via theblock_rows
kwarg with block 0 all-finite (register C-65 resolved); - conformance-suite negatives —
assert_reconcile_contractandassert_summarizer_contractare
shown to reject a deliberately non-conforming implementation (the leaf's C-51 envelope-negative
pattern, extended to the sibling packages); - reconcile mode-corners —
reconcile_result.modefor both-points and pre-tiled-cm inputs (both
ALIGNED_DRAWS); andReconciliationResultfrozen-ness (FrozenInstanceError).
- the non-finite (NaN / ±inf) fail-loud guard in
v1.7.0 — views_frames_reconcile (forecast reconciliation)
Adds a third sibling package, views_frames_reconcile, to the views-frames mono-wheel (alongside views_frames + views_frames_summarize). CONFORMANCE_FLOOR stays 1.0.0 — additive over the frozen v1 surface.
views_frames_reconcile (ADR-023)
Forecast reconciliation — make grid (pgm) predictions sum, per posterior draw, to their country (cm) totals — as a numpy-only, views_frames-only frame operation (the same kind of thing as views_frames_summarize, ADR-017).
ReconciliationModule(map_keys, map_vals).reconcile(cm_frame, pgm_frame)→ a new pgmPredictionFrame. The(time, priogrid_gid) → country_idmapping is injected as arrays, never fetched (ADR-014/ADR-023).reconcile_proportional(grid, country)— the per-draw top-down proportional method (zeros preserved, country totals authoritative, non-negative).assert_reconcile_contract(...)— the conformance suite.
How it got here
Faithfully relocated WET from views-postprocessing (where it was mis-homed and stranded) — the ported modules differ from the originals by import lines only. Proven correct by the frozen views-reporting torch-oracle parity tests and a 136-case new-vs-old np.array_equal bit-identity head-to-head.
Notes
- Charter: frame-reconciliation only; no IO, scoring, plotting, fetched mapping, or foreign
views_*. Import-DAGviews_frames_reconcile → {views_frames}. - WET before DRY:
grouping.py↔cross_level_alignfold deferred; the probabilistic upgrade (C-37) is a future sibling module. - Consumer repoint (views-models) + views-postprocessing deletion are the cross-repo cutover, gated on this release.
- Full changelog: CHANGELOG.md.
v1.6.0 — exceedance + worst-case expected_shortfall
main/PyPI jump 1.3.0 → 1.6.0 in one release. v1.4.0 and v1.5.0 were development-only milestones; v1.6.0 is the complete superset. CONFORMANCE_FLOOR stays 1.0.0 — every addition is additive over the frozen v1 surface.
Highlights (the 1.4.0 → 1.6.0 arc)
expected_shortfall — the worst-case tail mean (v1.6.0, ADR-022)
expected_shortfall(frame, tails) → (N, …, K): the per-row mean of the worst ⌈t·S⌉ draws (tail mean / CVaR) for each upper-tail fraction t. A coherent, subadditive worst-case risk measure and the companion to exceedance. max is never offered (highest-variance, non-reproducible). Tails are required per-call, in (0, 1] — the consumer's policy. Best-case ships no code (a low quantile + exceedance(frame, [0])).
exceedance — survival probability P(Y > c) (v1.5.0, ADR-021)
exceedance(frame, thresholds) → (N, …, K) + exceedance_reducer(c). Distribution-agnostic counting reducer; the flagship is P(Y > 0) = onset. Strict >; thresholds required per-call; geography-blind compose via aggregate_distributions.
Provenance + published envelope checker (v1.4.0, ADR-020)
FrameMetadata.run_id / data_version; assert_frame_envelope as a published conformance check; NaN-tolerant round-trip.
Fail-loud hardening (pre-release falsification audit)
Both new estimators reject non-finite draws (NaN or ±inf) via an np.isfinite guard — an inf draw (always an upstream bug) can no longer silently contaminate a worst-case or bless an exceedance probability.
Notes
- Single
views-frameswheel ships bothviews_frames(numpy-only data contract) +views_frames_summarize(sample-axis summarization). - Published via PyPI Trusted Publishing (
publish_package.yml). - Full changelog: CHANGELOG.md.