From 5c8a08ac74547b0a3b182fa09273940214bafe1b Mon Sep 17 00:00:00 2001 From: Polichinl Date: Mon, 2 Nov 2026 08:55:25 +0100 Subject: [PATCH 1/3] =?UTF-8?q?chore(deps):=20lift=20the=20views-frames=20?= =?UTF-8?q?cap=20to=20<4=20=E2=80=94=20measured,=20not=20assumed=20(#324)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pyproject.toml` pinned `views-frames = ">=2.0.0,<3"`. views-models' pod runners require views-frames >= 3.0.0, so the cap meant this repository could not be installed alongside the ensemble lane that produces its input. That is the real cost; it is not a correctness problem. The cap excluded a release that changes nothing here. The whole 3.0.0 MAJOR is in `views_frames_reconcile` (donor fill for empty-grid draws, views-frames ADR-030), which this repository does not import — reconciliation left in #62. `views_frames/io/arrow.py`, the only views_frames module that produces bytes we publish (`contract/wire/shard.py:34`), is byte-identical across the boundary: `git diff v2.0.0..v3.0.0 -- views_frames/io/` is empty. Proved the way the fixture README requires, rather than argued. Regenerating the ADR-013 §10 golden fixture under views-frames 3.0.0 at the pinned toolchain reproduces all five artifacts byte-identically — SHA256SUMS unchanged, the arrow shard still `203650fd…12c54`. Full suite on Python 3.11 with the resulting locked set: 527 passed, 3 skipped, 41 xfailed, 0 failed. The lock moves so CI asserts the new ceiling instead of leaving it a claim, and it carries views-pipeline-core 3.3.0 -> 3.4.0 with it: 3.3.0 capped `views-frames <3.0.0`, so frames 3.x is not reachable alone. Both land inside ranges already declared here. Floors stay at views-frames 2.0.0 and views-pipeline-core 3.0.0 deliberately — (frames 2.x + pipeline-core 3.3.x) is still a coherent resolution and nothing here needs 3.x. Also corrects the fixture README, which recorded pipeline-core's `<2.0.0` cap as a live blocker. It is not one any more, and a comment asserting a blocker that has been lifted is the shape that manufactured three false findings in this platform two days ago. Answers #324's question as asked ("can 2.x read shards published by 3.x?") by noting its premise does not hold: Hop-A does not read through views_frames at all. `contract/track_a_source.py:82-86` decodes the Track-A archive with zipfile + np.load + json.loads, so inbound shard bytes are a pipeline-core contract. The one views-frames serialization call in this repository is outbound. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ANY1CCy9Xo7zjMY4XJ69v9 --- poetry.lock | 16 ++++++++-------- pyproject.toml | 24 +++++++++++++++++++++++- tests/fixtures/wire_contract/README.md | 18 +++++++++++++++--- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/poetry.lock b/poetry.lock index d9690e3..d56a859 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3507,14 +3507,14 @@ frames = ["views-frames (>=1.10.2,<2)"] [[package]] name = "views-frames" -version = "2.0.0" +version = "3.0.0" description = "The VIEWS platform data-contract layer: immutable array+identifier frames (numpy only, root of the dependency DAG)." optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "views_frames-2.0.0-py3-none-any.whl", hash = "sha256:389feb16d6bda995d9f2914de6f5619a38ff37025a0022216eb65d44e03f0b48"}, - {file = "views_frames-2.0.0.tar.gz", hash = "sha256:de13c9713fdecbeb705d0698d285941ea1c614c37e80e8bc2ed7b68f83733ac5"}, + {file = "views_frames-3.0.0-py3-none-any.whl", hash = "sha256:0dab8ff3c374ac987734a1afce952db6db4538b9ee96b0fbe02b9f83a92c37e3"}, + {file = "views_frames-3.0.0.tar.gz", hash = "sha256:b9b6407d5eaf25ba5a49893e807548b1049d5e4eeb4127c4e288222b3ee415c2"}, ] [package.dependencies] @@ -3526,14 +3526,14 @@ docs = ["jupyterlab (>=4,<5)", "matplotlib (>=3.8,<4)", "nbmake (>=1.5,<2)", "py [[package]] name = "views-pipeline-core" -version = "3.3.0" +version = "3.4.0" description = "Core orchestration, data and model-management library for the VIEWS conflict forecasting platform." optional = false python-versions = "<3.15,>=3.11" groups = ["main"] files = [ - {file = "views_pipeline_core-3.3.0-py3-none-any.whl", hash = "sha256:7c0c7535ee9de7a74528bb64393621e379d57556ee125785d4d149a745dab515"}, - {file = "views_pipeline_core-3.3.0.tar.gz", hash = "sha256:8edf80123d35a626ce282aae8bac94c9b6a56cdf3b0163c0d98c9ddbfb5aa7d1"}, + {file = "views_pipeline_core-3.4.0-py3-none-any.whl", hash = "sha256:c560396f1ff880ea76da7365e75bbb6a49d4fa58b4518ab57021240cb29e553e"}, + {file = "views_pipeline_core-3.4.0.tar.gz", hash = "sha256:806c4410caab4562ca1542af604808eca4532c079c800fb3b7615171b1156599"}, ] [package.dependencies] @@ -3546,7 +3546,7 @@ pyarrow = ">=14.0.0,<17.0.0" pyprojroot = ">=0.3.0,<0.4.0" tqdm = ">=4.66,<5.0" views-evaluation = ">=2.0.0,<3.0.0" -views-frames = ">=1.10.2,<3.0.0" +views-frames = ">=3.0.0,<4.0.0" viewser = ">=6.6.4,<7.0.0" wandb = ">=0.18.7,<1.0" @@ -3723,4 +3723,4 @@ test = ["pytest", "websockets"] [metadata] lock-version = "2.1" python-versions = ">=3.11,<3.15" -content-hash = "e305efbfd4f9ed430ac7051fca50378ec49692af59354a03c2072ebf5cfaade6" +content-hash = "454f301b3fa62ea74ff8b906804cc6e02f758854d47d290f4fbe8ef3fb44ee46" diff --git a/pyproject.toml b/pyproject.toml index 599d77a..bec3db4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,29 @@ python = ">=3.11,<3.15" # import the SDK unguarded. Without the extra the FAO delivery fails **at import**, not # at runtime, and inside a dependency rather than in our own code. Register C-07. views-pipeline-core = {version = ">=3.0.0,<4.0.0", extras = ["appwrite"]} -views-frames = ">=2.0.0,<3" +# Widened from `<3` on 2026-11-02 (#324, filed from views-models). The cap was not +# harmless: views-models' pod runners require views-frames >= 3.0.0, so `<3` meant this +# repo could not be installed alongside the ensemble lane that produces its input. +# +# Safe because the 3.0.0 MAJOR is confined to `views_frames_reconcile` (donor fill for +# empty-grid draws, views-frames ADR-030), which this repo does not import — +# reconciliation left here in #62. `views_frames/io/arrow.py`, the only views_frames +# module that produces bytes we publish (`contract/wire/shard.py:34`), is BYTE-IDENTICAL +# across the boundary: `git diff v2.0.0..v3.0.0 -- views_frames/io/` is empty. Hop-A does +# not read through views_frames at all (`contract/track_a_source.py:82-86` decodes the +# archive with zipfile + np.load), so inbound shard bytes are a pipeline-core contract, +# not a views-frames one. +# +# **views-frames 3.x is not reachable alone:** pipeline-core 3.3.0 caps +# `views-frames <3.0.0`, so the lock moves pipeline-core 3.3.0 -> 3.4.0 with it. Both are +# inside the ranges already declared here. The floor stays at 2.0.0 and pipeline-core's at +# 3.0.0 deliberately — (frames 2.x + pipeline-core 3.3.x) is still a coherent resolution. +# +# Measured on Python 3.11 with the resulting locked set (views-frames 3.0.0, +# views-pipeline-core 3.4.0, pandas 1.5.3, pyarrow 16.1.0): **527 passed, 3 skipped, +# 41 xfailed, 0 failed**, including the ADR-013 §10 golden byte-parity fixture and the +# manager tests. The lock moves so CI asserts this rather than leaving it a claim. +views-frames = ">=2.0.0,<4.0.0" pyarrow = ">=16.1.0,<17.0.0" # pytest was never declared here; it arrived transitively through pipeline-core, which diff --git a/tests/fixtures/wire_contract/README.md b/tests/fixtures/wire_contract/README.md index bcab8c5..f95e7d8 100644 --- a/tests/fixtures/wire_contract/README.md +++ b/tests/fixtures/wire_contract/README.md @@ -46,6 +46,18 @@ fixture — but it does require proving that, the same way. equal the committed fixture (`203650fd…12c54`) — measured in an isolated environment at the pinned toolchain (pyarrow 16.1.0, numpy 1.26.4), where all 61 frames-dependent tests also pass at 2.0.0. So the views-frames 2.0.0 adoption (#286) is **not** a fixture re-vendor. It -is blocked only by views-pipeline-core, every published release of which (through 3.1.1) -pins `views-frames <2.0.0`. Recorded so the byte question is not re-opened when that -constraint widens. \ No newline at end of file +was blocked only by views-pipeline-core, every release published at the time (through +3.1.1) pinning `views-frames <2.0.0`. Recorded so the byte question is not re-opened when +that constraint widens. + +**That constraint has since widened, and the byte question was re-opened and answered the +same way (2026-11-02, #324).** Regenerating with `scripts/build_wire_fixture.py` under +**views-frames 3.0.0** at the pinned toolchain (pyarrow 16.1.0) reproduces **all five +artifacts byte-identically** — `SHA256SUMS` is unchanged and the arrow shard still hashes +`203650fd…12c54`. Expected, because `views_frames/io/arrow.py` is byte-identical between +`v2.0.0` and `v3.0.0` (`git diff v2.0.0..v3.0.0 -- views_frames/io/` is empty): the whole +3.0.0 MAJOR is in `views_frames_reconcile`, which this repo does not import. The full +suite passes at that version on Python 3.11 — 527 passed, 3 skipped, 41 xfailed, 0 failed +— with the lock also moving views-pipeline-core 3.3.0 -> 3.4.0, since 3.3.0 was the +release that still capped `views-frames <3.0.0`. So 3.0.0 is not a fixture re-vendor +either, and the pin is now `>=2.0.0,<4.0.0`. \ No newline at end of file From 5f9b3d5a2af79403b1ca70961865e4b0b5594e98 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Mon, 2 Nov 2026 09:02:09 +0100 Subject: [PATCH 2/3] fix: the command I offered as proof was a check that could not fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review found two factual defects in 5c8a08a's own prose. Both are real. **1. The proof command was vacuous.** Both files quoted git diff v2.0.0..v3.0.0 -- views_frames/io/ # "is empty" as the evidence that the arrow writer is unchanged. views-frames uses a **src layout**: the package is at `src/views_frames/io/`. That pathspec matches nothing in either tree, so git prints nothing and exits 0 — it returns empty whether or not arrow.py changed. Demonstrated: the same form aimed at `views_frames/conformance/`, a directory that definitely changed in 3.0.0, is also empty. This is the guards-that-cannot-fire family, and the placement makes it worse than a typo. The fixture README exists so "the byte question is not re-opened", so the next person raising this pin would have re-run a command that cannot fail and read it as a pass. Corrected to `src/views_frames/io/`, and the README now carries a **control** line (`-- src/views_frames/`, which must be NON-empty) with the reason: an empty result from `git diff` does not distinguish "nothing changed" from "nothing matched". The paragraph says plainly that its own first draft had the bug. The underlying claim was true. `git diff v2.0.0..v3.0.0 -- src/views_frames/io/` is genuinely empty; arrow.py is byte-identical across the tags. The byte-parity conclusion and the regenerated fixture stand. **2. "The 3.0.0 MAJOR is confined to `views_frames_reconcile`" was overstated.** The MAJOR also touches `views_frames/conformance/__init__.py`, which moves its published `CONFORMANCE_FLOOR` from 2.0.0 to 3.0.0. Accurate claim, now written: the *behavioural* change is confined to the reconcile package; the floor constant also moves, and is inert here because nothing in this repo reads it (`grep -rn CONFORMANCE_FLOOR` returns nothing). The non-import claim was correct as written and is unchanged — `views_frames_reconcile` has no importer here. Not rewriting history to fix this: 5c8a08a's message still quotes the bad pathspec. Force-pushing is the operator's call in this repository, and the record of the correction is more useful than a clean one. Unchanged: 527 passed, 3 skipped, 41 xfailed, 0 failed on Python 3.11 with the locked set; ruff clean; `poetry check --lock` clean. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ANY1CCy9Xo7zjMY4XJ69v9 --- pyproject.toml | 22 ++++++++++++-------- tests/fixtures/wire_contract/README.md | 28 ++++++++++++++++++++------ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bec3db4..876f115 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,14 +21,20 @@ views-pipeline-core = {version = ">=3.0.0,<4.0.0", extras = ["appwrite"]} # harmless: views-models' pod runners require views-frames >= 3.0.0, so `<3` meant this # repo could not be installed alongside the ensemble lane that produces its input. # -# Safe because the 3.0.0 MAJOR is confined to `views_frames_reconcile` (donor fill for -# empty-grid draws, views-frames ADR-030), which this repo does not import — -# reconciliation left here in #62. `views_frames/io/arrow.py`, the only views_frames -# module that produces bytes we publish (`contract/wire/shard.py:34`), is BYTE-IDENTICAL -# across the boundary: `git diff v2.0.0..v3.0.0 -- views_frames/io/` is empty. Hop-A does -# not read through views_frames at all (`contract/track_a_source.py:82-86` decodes the -# archive with zipfile + np.load), so inbound shard bytes are a pipeline-core contract, -# not a views-frames one. +# Safe because the 3.0.0 MAJOR's *behavioural* change is confined to +# `views_frames_reconcile` (donor fill for empty-grid draws, views-frames ADR-030), which +# this repo does not import — reconciliation left here in #62. That is not the only file +# the MAJOR touches: `views_frames/conformance/__init__.py` also moves its published +# `CONFORMANCE_FLOOR` from 2.0.0 to 3.0.0. Nothing in this repo reads that constant +# (`grep -rn CONFORMANCE_FLOOR` returns nothing), so the move is inert here. +# +# `views_frames/io/arrow.py`, the only views_frames module that produces bytes we publish +# (`contract/wire/shard.py:34`), is BYTE-IDENTICAL across the boundary: +# `git diff v2.0.0..v3.0.0 -- src/views_frames/io/` is empty. **Mind the `src/`** — +# views-frames uses a src layout, so the same pathspec without it matches nothing and +# returns empty whatever changed. Hop-A does not read through views_frames at all +# (`contract/track_a_source.py:82-86` decodes the archive with zipfile + np.load), so +# inbound shard bytes are a pipeline-core contract, not a views-frames one. # # **views-frames 3.x is not reachable alone:** pipeline-core 3.3.0 caps # `views-frames <3.0.0`, so the lock moves pipeline-core 3.3.0 -> 3.4.0 with it. Both are diff --git a/tests/fixtures/wire_contract/README.md b/tests/fixtures/wire_contract/README.md index f95e7d8..7c3496d 100644 --- a/tests/fixtures/wire_contract/README.md +++ b/tests/fixtures/wire_contract/README.md @@ -55,9 +55,25 @@ same way (2026-11-02, #324).** Regenerating with `scripts/build_wire_fixture.py` **views-frames 3.0.0** at the pinned toolchain (pyarrow 16.1.0) reproduces **all five artifacts byte-identically** — `SHA256SUMS` is unchanged and the arrow shard still hashes `203650fd…12c54`. Expected, because `views_frames/io/arrow.py` is byte-identical between -`v2.0.0` and `v3.0.0` (`git diff v2.0.0..v3.0.0 -- views_frames/io/` is empty): the whole -3.0.0 MAJOR is in `views_frames_reconcile`, which this repo does not import. The full -suite passes at that version on Python 3.11 — 527 passed, 3 skipped, 41 xfailed, 0 failed -— with the lock also moving views-pipeline-core 3.3.0 -> 3.4.0, since 3.3.0 was the -release that still capped `views-frames <3.0.0`. So 3.0.0 is not a fixture re-vendor -either, and the pin is now `>=2.0.0,<4.0.0`. \ No newline at end of file +`v2.0.0` and `v3.0.0`, and because the 3.0.0 MAJOR's behavioural change is confined to +`views_frames_reconcile`, which this repo does not import. (The MAJOR does also touch +`views_frames/conformance/__init__.py`, which moves its published `CONFORMANCE_FLOOR` to +3.0.0 — inert here, since nothing in this repo reads that constant.) The full suite passes +at that version on Python 3.11 — 527 passed, 3 skipped, 41 xfailed, 0 failed — with the +lock also moving views-pipeline-core 3.3.0 -> 3.4.0, since 3.3.0 was the release that +still capped `views-frames <3.0.0`. So 3.0.0 is not a fixture re-vendor either, and the +pin is now `>=2.0.0,<4.0.0`. + +**If you are the next person raising this pin, run the check like this.** views-frames +uses a **src layout**, so the pathspec needs `src/`: + +``` +git -C diff v2.0.0..v3.0.0 -- src/views_frames/io/ # empty -> arrow.py is unchanged +git -C diff v2.0.0..v3.0.0 -- src/views_frames/ # NOT empty -> the control +``` + +**Run the second one too.** `git diff` prints nothing and exits 0 for a pathspec that +matches no file in either tree, so `-- views_frames/io/` (no `src/`) comes back empty +whatever changed, and reads exactly like a pass. The first draft of this very paragraph +quoted that form as its proof. The control line is what tells you the command can still +speak — if it is also empty, your pathspec is wrong, not the tree. \ No newline at end of file From 5d271cb2a9a99e57ec6aa21beb5803b232ff7342 Mon Sep 17 00:00:00 2001 From: Polichinl Date: Mon, 2 Nov 2026 09:04:31 +0100 Subject: [PATCH 3/3] refactor(docs): one home for the byte question, a pointer at the pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /review-diff finding (Category D, duplication): the 3.0.0 rationale was stated in full twice — once in the `pyproject.toml` pin comment and once in `tests/fixtures/wire_contract/README.md`. Two copies of one explanation, which is the shape this repo has been moving away from. The fixture README is the right home and already says so: it carries the 1.10.2 and 2.0.0 byte measurements as a series and exists specifically so "the byte question is not re-opened". So the pin comment now states the decision and its pin-specific facts — the operational reason the cap had to go, the pipeline-core 3.4.0 coupling, why the floors stay — and points at that file for the evidence, with the instruction to read it before raising the pin again. Drops the duplicated arrow.py / CONFORMANCE_FLOOR paragraph and the test counts from `pyproject.toml`; both live in the README, dated, where a stale number is legible as history rather than read as a current claim. 28 comment lines to 18. No behaviour change; the constraint is untouched. Still 527 passed, 3 skipped, 41 xfailed, 0 failed on Python 3.11 with the locked set; ruff and `poetry check --lock` clean. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ANY1CCy9Xo7zjMY4XJ69v9 --- pyproject.toml | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 876f115..62d2521 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,30 +21,20 @@ views-pipeline-core = {version = ">=3.0.0,<4.0.0", extras = ["appwrite"]} # harmless: views-models' pod runners require views-frames >= 3.0.0, so `<3` meant this # repo could not be installed alongside the ensemble lane that produces its input. # -# Safe because the 3.0.0 MAJOR's *behavioural* change is confined to -# `views_frames_reconcile` (donor fill for empty-grid draws, views-frames ADR-030), which -# this repo does not import — reconciliation left here in #62. That is not the only file -# the MAJOR touches: `views_frames/conformance/__init__.py` also moves its published -# `CONFORMANCE_FLOOR` from 2.0.0 to 3.0.0. Nothing in this repo reads that constant -# (`grep -rn CONFORMANCE_FLOOR` returns nothing), so the move is inert here. -# -# `views_frames/io/arrow.py`, the only views_frames module that produces bytes we publish -# (`contract/wire/shard.py:34`), is BYTE-IDENTICAL across the boundary: -# `git diff v2.0.0..v3.0.0 -- src/views_frames/io/` is empty. **Mind the `src/`** — -# views-frames uses a src layout, so the same pathspec without it matches nothing and -# returns empty whatever changed. Hop-A does not read through views_frames at all -# (`contract/track_a_source.py:82-86` decodes the archive with zipfile + np.load), so -# inbound shard bytes are a pipeline-core contract, not a views-frames one. +# Why 3.x is safe for us is recorded ONCE, in `tests/fixtures/wire_contract/README.md` — +# the home of the byte question, already carrying the 1.10.2 and 2.0.0 measurements in the +# same series. Short version: the MAJOR's behavioural change is in `views_frames_reconcile`, +# which this repo does not import, and the arrow writer is byte-identical across the +# boundary. **Read that file before raising this pin again.** It names the check to run and +# the control that tells you the check is working — the first draft of this change offered +# a pathspec that silently matched nothing. # # **views-frames 3.x is not reachable alone:** pipeline-core 3.3.0 caps # `views-frames <3.0.0`, so the lock moves pipeline-core 3.3.0 -> 3.4.0 with it. Both are # inside the ranges already declared here. The floor stays at 2.0.0 and pipeline-core's at # 3.0.0 deliberately — (frames 2.x + pipeline-core 3.3.x) is still a coherent resolution. -# -# Measured on Python 3.11 with the resulting locked set (views-frames 3.0.0, -# views-pipeline-core 3.4.0, pandas 1.5.3, pyarrow 16.1.0): **527 passed, 3 skipped, -# 41 xfailed, 0 failed**, including the ADR-013 §10 golden byte-parity fixture and the -# manager tests. The lock moves so CI asserts this rather than leaving it a claim. +# The lock moves rather than staying at 2.0.0 so CI asserts this ceiling instead of +# leaving it a claim. views-frames = ">=2.0.0,<4.0.0" pyarrow = ">=16.1.0,<17.0.0"