From 90c3ca8bf3fddde409c8c29f57c94b2a0622c585 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Sat, 15 Aug 2026 14:04:07 +0200 Subject: [PATCH 1/2] release: 1.1.1, and make the guard that protects the release order live in CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit THE VERSION. 1.1.1, a patch, measured rather than assumed. The only external imports of this package anywhere in the org are UNFAOPostProcessorManager and CRAFDPostProcessorManager, from views-models' un_fao/main.py and un_crafd/main.py, and both paths are unchanged. Nothing outside this repo imports track_a_source, frames_for_target, store_port or _ContractStorePort — so #269's frames_for_target signature change breaks no consumer. No new capability; two bug fixes and an internal refactor. It is earned this time: three merged stories changed code under views_postprocessing/ for the first time since 1.1.0. On 2026-08-13 the trees were byte-identical and no tag was cut, which is why that release did not happen. The version still has exactly one home, pyproject.toml:3 — no __version__, no CHANGELOG. Re-checked rather than remembered, because C-80 and C-82 were both a version declared twice with a guard on one copy. THE GUARD WAS INERT WHERE IT MATTERED. tests/test_release_version.py exists because tag 1.1.0 was cut at main while pyproject.toml still said 1.0.0. But run_pytest.yml checks out with actions/checkout@v3 and no fetch-depth, so CI fetches NO TAGS and both of its tests skip there. It has only ever run on a laptop — a guard against mis-cutting a release that is absent from the one place a release is verified. Demonstrated, not asserted. A shallow --no-tags clone (CI's default shape) skips both with "no release tags in this checkout (a shallow clone, or none cut yet)". A full clone runs them, and setting pyproject back to 1.0.0 against the 1.1.0 tag fails with "the newest release tag is 1.1.0 but pyproject.toml declares 1.0.0" — the original defect, caught. fetch-depth: 0 is for the tags, not the history; the comment says so, because the next person to see a full fetch in a small repo's CI will reasonably wonder. Suite 457 passed / 1 skipped / 39 xfailed, ruff clean. The release guard locally: the newest-tag check passes (1.1.0 <= 1.1.1), the tagged-commit check skips because HEAD is not tagged yet — which is the correct state before S3. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/run_pytest.yml | 8 ++++++++ pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml index f259bf3..a782614 100644 --- a/.github/workflows/run_pytest.yml +++ b/.github/workflows/run_pytest.yml @@ -18,6 +18,14 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + with: + # `fetch-depth: 0` for the TAGS, not the history. The default shallow checkout + # fetches none, so `git tag -l` is empty and BOTH guards in + # tests/test_release_version.py skip — the pair written because tag 1.1.0 was cut + # while pyproject.toml still said 1.0.0. A guard against mis-cutting a release + # that only ever runs on the maintainer's laptop is half a guard, and the half + # that is missing is the one watching the moment it matters. + fetch-depth: 0 # ── sibling checkouts: what CI can verify that a bare checkout cannot ──────── # diff --git a/pyproject.toml b/pyproject.toml index 043be9d..f42e9a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "views-postprocessing" -version = "1.1.0" +version = "1.1.1" description = "" authors = [ "Dylan Pinheiro ", From 73340a2cf7cfdfc361a39f7f3b8ed15ed2a343c5 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Sat, 15 Aug 2026 14:14:03 +0200 Subject: [PATCH 2/2] fix(release-guard): scope it to this line of history, and two false claims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review of the release prep. Making the guard live in CI was right; it would have made it fire on branches that had done nothing wrong. THE GUARD ASKED THE WRONG QUESTION. `git tag -l` lists tags on every branch, so the check was "has a release been cut anywhere", not "has one been cut from THIS line of history without the bump". Invisible while CI fetched no tags; a false alarm the moment it started. Reproduced in a clone: with 1.1.1 tagged on the release line, a branch still declaring 1.1.0 — an honest feature branch, or a hotfix cut from 1.1.0 — fails with "the newest release tag is 1.1.1 but pyproject.toml declares 1.1.0". It has done nothing wrong; the tag simply is not on its history. `git tag --merged HEAD` asks the right question. Same clone, same branch: tags anywhere are 1.0.0 1.1.0 1.1.1, tags reachable are 1.0.0 1.1.0, and it passes. On the tagged line it still bites both ways — setting pyproject back to 1.1.0 with 1.1.1 reachable fails both guards with their own messages. ADR-014 §3 prefers a false negative to a false alarm. I was about to activate a guard whose first act would have been to redden unrelated work, and a guard that does that is one someone deletes. "C-80 AND C-82 WERE BOTH A VERSION DECLARED TWICE WITH A GUARD ON ONE COPY" IS FALSE. C-80 is the doc-accuracy scan exempting ADRs and CICs; C-82 is governance prose carrying numbers nothing checks — the same class as this, a number with no guard, but about the register and CIC front matter, not a version declared twice. The claim originated in this test's own docstring on 2026-08-13 and I repeated it into a release PR without reading either entry. Corrected at the source, with a note saying so, since the docstring is where the next person will read it. "THREE MERGED STORIES CHANGED CODE UNDER views_postprocessing/" IS FALSE. Two did — #268 (ff0278b) and #269 (70f25f0). #265 (0d38a71) touched tests and the register only. Three stories merged; two changed package code. The version conclusion is unaffected: a patch was earned by those two. Suite 458 passed / 1 skipped / 39 xfailed, ruff clean. Co-Authored-By: Claude Opus 5 (1M context) --- tests/test_release_version.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/test_release_version.py b/tests/test_release_version.py index 731bc69..3688e18 100644 --- a/tests/test_release_version.py +++ b/tests/test_release_version.py @@ -7,8 +7,14 @@ Measured 2026-08-13: tag `1.1.0` was cut at `main` while `pyproject.toml` still said `1.0.0`, so an install from that tag reported the previous release. Caught before any -consumer pinned. Third time in this arc a version has been declared twice with a guard on -one copy (register C-80, C-82). +consumer pinned, and the tag was re-pointed. + +*(This docstring used to add "third time in this arc a version has been declared twice +with a guard on one copy (register C-80, C-82)". Corrected 2026-08-15: neither entry says +that. C-80 is the doc-accuracy scan exempting ADRs and CICs; C-82 is governance prose +carrying numbers nothing checks — the same **class** as this, a number with no guard, but +about the register and CIC front matter, not about a version declared twice. The claim was +repeated into a release PR before anyone read the entries it cited.)* """ import re @@ -63,9 +69,18 @@ def test_the_newest_release_tag_is_not_ahead_of_the_declared_version(): This is the direction that actually bit: the tag moved, the file did not. Runs on every commit, not only tagged ones, so the gap is visible the moment it opens. + + **Tags reachable from HEAD, not every tag in the repository.** ``git tag -l`` lists + tags on every branch, which asks the wrong question: whether a release was cut + *anywhere*, rather than whether one was cut from *this line of history* without the + bump. The difference was invisible while CI fetched no tags at all, and would have + become a false alarm the moment it started: tag `1.1.1` on `main` would redden every + branch still declaring `1.1.0` — a long-lived feature branch, a hotfix cut from + `1.1.0` — none of which has done anything wrong. ADR-014 §3 prefers a false negative + to a false alarm, and a guard that reddens honest branches is one someone deletes. """ out = subprocess.run( - ["git", "-C", str(_REPO), "tag", "-l", "--sort=-v:refname"], + ["git", "-C", str(_REPO), "tag", "--merged", "HEAD", "--sort=-v:refname"], capture_output=True, text=True, check=False, timeout=30, ) releases = [t for t in out.stdout.split() if re.fullmatch(r"\d+\.\d+\.\d+", t)]