From 3cbfbc3d8287fcfa07834b2c371cc7203b665c33 Mon Sep 17 00:00:00 2001 From: arpan Date: Mon, 14 Sep 2026 10:12:15 +0530 Subject: [PATCH] The adapter tag format, corrected and pinned: no `v` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caught while pre-flighting the tags for the 1.1.0 adapter release, by running publish.yml's own `case` statement rather than reading it. `publish.yml` strips a leading `v` from a KERNEL tag and reads an ADAPTER tag as everything after the last hyphen. So the two formats are different and the difference is fatal one way: adapters-langgraph-v1.1.0 tag=v1.1.0 version=1.1.0 REFUSED adapters-langgraph-1.1.0 tag=1.1.0 version=1.1.0 ACCEPTED adapters-langgraph-1.1 tag=1.1 version=1.1.0 ACCEPTED v0.10.0 tag=0.10.0 version=0.10.0 ACCEPTED The assertion message added with `adapters/PUBLISHED.toml` told a reader to push `adapters--v`, which is the refused form. It is the message somebody reads at exactly the moment they are about to tag, so it is now the accepted form. `test_the_tag_an_adapter_release_needs_is_one_publish_yml_accepts` transcribes the workflow's two rules and runs them against the version in the tree, asserting both that the release's tag is accepted and that the `v` form is still refused. If publish.yml ever accepts the `v` form, that assertion fails and the guidance here changes with it, rather than silently becoming wrong in the other direction. `MAJOR.MINOR` is accepted as well as the full version, which is SPEC-v0.5 §6.2: adapters version by version line and the distribution carries the patch. Signed-off-by: arpan --- adapters/PUBLISHED.toml | 6 ++++++ tests/test_packaging.py | 47 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/adapters/PUBLISHED.toml b/adapters/PUBLISHED.toml index 768ac5f0..e3a9fc70 100644 --- a/adapters/PUBLISHED.toml +++ b/adapters/PUBLISHED.toml @@ -16,6 +16,12 @@ # the tree: if an adapter's range has moved away from what was published, its version must have # moved too, or the release is one nobody can install. # +# The tag carries NO `v`. `publish.yml` reads an adapter tag as everything after the last hyphen, +# so `adapters-langgraph-1.1.0` names 1.1.0 and `adapters-langgraph-v1.1.0` names `v1.1.0` and is +# refused. The `v` prefix belongs to kernel tags, where the workflow strips it. `MAJOR.MINOR` is +# accepted too, per SPEC-v0.5 §6.2. `test_the_tag_an_adapter_release_needs_is_one_publish_yml_accepts` +# runs the workflow's own rules against the version here, so this comment cannot go stale quietly. +# # It is deliberately hand-written rather than fetched. A test that asked PyPI would need the # network, and `SPEC-v0.4 §3.9`'s discipline applies: a check that cannot run offline is a check # that gets skipped in the run that mattered. diff --git a/tests/test_packaging.py b/tests/test_packaging.py index 816dd8fa..90151cc8 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -809,6 +809,46 @@ def test_the_throwaway_sector_configuration_ships_nowhere(): ADAPTER_DIRECTORIES = ("langgraph", "openai-agents") +@pytest.mark.parametrize("adapter", ADAPTER_DIRECTORIES) +def test_the_tag_an_adapter_release_needs_is_one_publish_yml_accepts(adapter): + """The tag format differs between the kernel and an adapter, and the difference is a trap. + + `publish.yml` strips a leading `v` from a kernel tag, and reads an adapter tag as **everything + after the last hyphen**. So `v0.10.0` names version `0.10.0`, while `adapters-langgraph-v1.1.0` + names `v1.1.0` and is refused against `1.1.0`. The `v` that is required on one is fatal on the + other, and the failure happens at the tag, after it is pushed. + + This runs the workflow's own two rules against the version in the tree, so the tag a release + actually needs is asserted rather than remembered. `MAJOR.MINOR` is accepted as well as the + full version, which is `SPEC-v0.5 §6.2`: adapters version by version line and the distribution + carries the patch. + """ + import tomllib as _tomllib + + manifest = REPO_ROOT / "adapters" / adapter / "pyproject.toml" + if not manifest.exists(): + pytest.skip("adapters/ is not in this distribution, which SPEC-v0.5 §6.1 requires") + with manifest.open("rb") as handle: + version = _tomllib.load(handle)["project"]["version"] + + def accepted(ref: str) -> bool: + """`publish.yml`'s `case` statement, transcribed.""" + if ref.startswith("v"): + return ref[1:] == version + tag = ref.rsplit("-", 1)[-1] + return version == tag or version.startswith(tag + ".") + + major_minor = ".".join(version.split(".")[:2]) + assert accepted(f"adapters-{adapter}-{version}"), ( + f"adapters-{adapter}-{version} is the tag this release needs and publish.yml refuses it" + ) + assert accepted(f"adapters-{adapter}-{major_minor}"), "the MAJOR.MINOR form is SPEC-v0.5 §6.2's" + assert not accepted(f"adapters-{adapter}-v{version}"), ( + "a `v` prefix on an adapter tag now resolves; if publish.yml changed, the guidance in " + "this file and in adapters/PUBLISHED.toml changes with it" + ) + + @pytest.mark.parametrize("adapter", ADAPTER_DIRECTORIES) def test_a_widened_kernel_range_is_not_shipped_without_a_new_version(adapter): """The test below checks the range in the tree. Nothing checked what is on PyPI. @@ -824,6 +864,11 @@ def test_a_widened_kernel_range_is_not_shipped_without_a_new_version(adapter): moved away from what was published, its **version** must have moved too, or the widening is one nobody can install. Hand-written rather than fetched, because a check that needs the network is a check that gets skipped in the run that mattered. + + **The tag carries no `v`.** `publish.yml` reads an adapter tag as everything after the last + hyphen, so `adapters-langgraph-v1.1.0` yields `v1.1.0` and is refused against version `1.1.0`. + The `v` prefix belongs to kernel tags, where the workflow strips it. The message below said + otherwise and would have sent a reader to a tag the workflow rejects. """ import tomllib as _tomllib @@ -846,7 +891,7 @@ def test_a_widened_kernel_range_is_not_shipped_without_a_new_version(adapter): assert project["version"] != published["version"], ( f"adapters/{adapter} declares ctrlrun{declared} but {published['version']} on PyPI " f"declares ctrlrun{published['kernel']}, and the version has not moved. Bump it and tag " - f"`adapters-{adapter}-v`, then record the new version and range in " + f"`adapters-{adapter}-`, then record the new version and range in " "adapters/PUBLISHED.toml. A widened range nobody can install is not a widened range." )