From e8f2b4af66db0c8faedbd0ef13fc8ec2d81c2964 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Tue, 29 Sep 2026 22:20:31 +0200 Subject: [PATCH] =?UTF-8?q?release:=201.4.0=20=E2=80=94=20the=20findabilit?= =?UTF-8?q?y=20fix,=20which=20is=20unreachable=20until=20it=20is=20tagged?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps 1.3.0 -> 1.4.0. MINOR rather than PATCH by the same reasoning as 1.2.0: a previously-passing delivery can now stop. The guard refuses in four situations instead of two. That is a behaviour change a launcher must be told about, not a patch. Cut now rather than at convenience because the launcher installs from a git TAG (views-models tools/launcher/postprocessor.sh:57), not from PyPI and not from poetry.lock. Until this tag exists, the fix for today's unservable delivery is merged, reviewed, tested — and reachable by nothing. 1.2.0 sat unpinned for three weeks for exactly this reason. The changelog entry leads with the consequence for someone still on an older pin: a delivery can complete successfully and be unservable. Then what changed, then why taking it promptly is worth it — the deterministic-re-run case, where the dedup path that took one object takes all 110 and the documented remedy for a torn run is what triggers it. Recorded there as known-and-not-ours: pipeline-core's get_latest_file_id documents "newest by creation timestamp" over an unsorted result, which the SELECTION half of the guard has relied on since August. This release removes the equivalent assumption from the per-object half. The upstream half is filed there. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ANY1CCy9Xo7zjMY4XJ69v9 --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 021c90b..ade3860 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,64 @@ This file exists because the version number was the only signal a consumer got (register C-111). Releases before 1.2.0 are summarised from their tags rather than reconstructed in detail. +## 1.4.0 — 2026-09-29 + +**Fixes an unservable delivery.** The first live UN-FAO run, earlier today, uploaded +109 of 110 objects, reported success, and was refused by views-faoapi at ingest. If you +are pinned below this version, a delivery can still complete successfully and be +unservable. + +### What was wrong + +The C-94 findability guard checked **two** things — the run manifest and the historical +artifact — by querying the newest document per *category*. Every wire object carries +`category="forecast"` and the manifest is uploaded last, so that query always returned +the manifest. The GAUL sidecar and the 108 shards were never asked about. + +The sidecar's bytes were identical to the previous run's, the content-addressed store +correctly declined a second copy, the metadata document was updated against the **old** +file, and the upload returned a real file id for the wrong document. Nothing observed it. + +### What changed for a consumer + +The guard now asks two questions instead of one, and **refuses in four situations rather +than two**. A delivery that previously completed can now stop: + +| | | +|---|---| +| *selection* | does the consumer's own query land on this run? — unchanged | +| *per-object* | does **every** uploaded artefact resolve under its own filename? — new | + +Both raise the existing `DeliveryNotFindableError`; no new exception types. A failed +*check* still reports `FindabilityUnverifiedError` rather than condemning the delivery. + +**If one of these fires after upgrading it is reporting a condition that was already +wrong and already invisible.** The refusal names every object that does not resolve, and +says so explicitly when nothing in the run is servable. + +### Why this is worth taking promptly + +Pooling upstream is deterministic, and every artefact's filename embeds the run id. So a +**re-run** writes new filenames over identical bytes, and the deduplication path that +took one object takes **all 110 at once** — the store creates the documents, the count is +right, and nothing is servable. Re-running is the documented remedy for a torn run, which +makes this the realistic case rather than the exotic one. + +### Also in this release + +- The store port gained a fifth method, `documents()`, and its documented duck-typing + contract now lists it. A datastore built to the previous docstring would have raised + `AttributeError` mid-delivery. +- `deliver_run`'s summary carries the upload ledger out, as `uploaded_objects`. + +### Known, and not ours + +`views-pipeline-core`'s `get_latest_file_id` documents *"the newest matching file based on +creation timestamp"* and takes the first element of an unsorted result. The *selection* +half of the guard has relied on that since August and can in principle raise a false +alarm. This release removes the equivalent assumption from the per-object half, which no +longer depends on document order at all. The upstream half is filed in pipeline-core. + ## 1.3.0 — 2026-09-19 **No new failure modes.** A launcher that ran 1.2.0 sees nothing new stop. This release diff --git a/pyproject.toml b/pyproject.toml index 04cc622..599d77a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "views-postprocessing" -version = "1.3.0" +version = "1.4.0" description = "" authors = [ "Dylan Pinheiro ",