From 6849d9a82ed288b995c182dc3e4d52a1c41230fc Mon Sep 17 00:00:00 2001 From: chaandannn Date: Wed, 19 Aug 2026 15:35:37 -0500 Subject: [PATCH 1/2] fix(shim): the re-exec has been sitting unpublished since it was written A reviewer on a directory submission said uvx nable does not start on machines whose default Python is under 3.11. I checked the source, saw the re-exec, and was ready to tell them it was fixed. Then I ran it: $ uvx --python 3.10 nable --version nable needs Python 3.11 or newer. This is Python 3.10. exit 1 They were right. The published nable 0.1.3 contains zero occurrences of _reexec_under_managed_python. This repo contains two. shim/pyproject.toml still declared 0.1.3, a version already on PyPI, so there was nothing left to publish the change under. The Ctrl-C handler from #108 is stranded in the same file. Silent by construction: publishing fires on a shim-v* tag, nobody pushed one, and a manual run would have hit skip-existing and reported success while uploading nothing. Fourth instance of one shape this week. The SBOM described an eleven-week-old release. The Releases feed showed one entry against 208 on PyPI. The MCP registry carries a duplicate 36 releases behind. Now this. Every one looked healthy because nothing 404s, and every one was found by someone stumbling into it rather than by a check. So this adds the check. A digest of nable_shim.py is locked to the version it will publish under, and editing the file fails the suite until the version moves, which is the moment to remember the tag. Four mutations caught: editing without bumping, drifting the version from the lock, reverting to the taken 0.1.3, and deleting the re-exec outright. Bumps to 0.1.4. Tag shim-v0.1.4 to actually ship it. Co-Authored-By: Claude Opus 5 --- shim/pyproject.toml | 2 +- tests/test_shim_ships_what_the_repo_has.py | 89 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/test_shim_ships_what_the_repo_has.py diff --git a/shim/pyproject.toml b/shim/pyproject.toml index 8813d12a..77f83ca8 100644 --- a/shim/pyproject.toml +++ b/shim/pyproject.toml @@ -35,7 +35,7 @@ build-backend = "hatchling.build" # all: the same staleness trap 0.1.1 fixed, one layer up. [project] name = "nable" -version = "0.1.3" +version = "0.1.4" description = "Ask Claude about your cloud and AI bill. Brand alias for finops-mcp." readme = "README.md" requires-python = ">=3.9" diff --git a/tests/test_shim_ships_what_the_repo_has.py b/tests/test_shim_ships_what_the_repo_has.py new file mode 100644 index 00000000..c63df5a2 --- /dev/null +++ b/tests/test_shim_ships_what_the_repo_has.py @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: Apache-2.0 +"""A shim fix that never reached PyPI is a shim fix that did not happen. + +Measured 2026-08-19 by running the exact scenario a reviewer flagged on a +directory submission: + + $ uvx --python 3.10 nable --version + nable needs Python 3.11 or newer. This is Python 3.10. + Run this instead: uvx --python 3.12 nable + exit 1 + +That message was supposed to be gone. nable_shim.py in this repo re-execs +itself under a uv-managed 3.12 rather than asking the user to retype the +command, and the Ctrl-C handler that keeps a traceback off that path merged in +PR #108. None of it reached anyone. shim/pyproject.toml still declared 0.1.3, +which was already on PyPI, so there was no version left to publish under. The +published artifact contains zero occurrences of _reexec_under_managed_python. +This repo contains two. + +The failure is silent by construction. Publishing fires on a shim-v* tag, +nobody pushed one, and a manual run would have hit skip-existing and reported +success while uploading nothing. + +Fourth instance of one shape in a week: the SBOM described an eleven-week-old +release, the GitHub Releases feed froze at one entry against 208 on PyPI, the +MCP registry carried a duplicate 36 releases behind, and now the shim. All four +looked healthy because nothing 404s. + +So the lock below is deliberately annoying. Edit nable_shim.py and this fails +until the version moves and the digest is updated, which is the moment to +remember the tag. No network, so it fails in CI exactly as it fails locally. +""" +from __future__ import annotations + +import hashlib +import pathlib +import re + +SHIM = pathlib.Path(__file__).resolve().parents[1] / "shim" + +# Bump BOTH when nable_shim.py changes, then push a shim-v tag. +PUBLISHED_UNDER = "0.1.4" +SOURCE_SHA256 = "85a33fb6f03918f4fd43153d4f64e813faf13b15f09578e89d48d78c9c0e19a9" + + +def _declared_version() -> str: + return re.search(r'^version = "([^"]+)"', + (SHIM / "pyproject.toml").read_text(), re.M).group(1) + + +def test_the_shim_source_matches_the_version_it_will_publish_under(): + actual = hashlib.sha256((SHIM / "nable_shim.py").read_bytes()).hexdigest() + version = _declared_version() + + if actual != SOURCE_SHA256: + raise AssertionError( + "shim/nable_shim.py changed since it was last locked.\n" + f" locked digest : {SOURCE_SHA256}\n" + f" actual digest : {actual}\n" + f" declared vers : {version}\n\n" + "Publishing it takes three steps, and skipping any one ships " + "nothing while looking fine:\n" + " 1. raise version in shim/pyproject.toml\n" + " 2. update PUBLISHED_UNDER and SOURCE_SHA256 here\n" + " 3. push a shim-v tag, which is what actually publishes\n\n" + "Step 3 is the one that was missed. The previous edit sat " + "unpublished under a version already taken on PyPI.") + + assert version == PUBLISHED_UNDER, ( + f"shim/pyproject.toml declares {version} but this lock says " + f"{PUBLISHED_UNDER}. They have to agree, or the digest is vouching for " + "a version nobody intends to publish.") + + +def test_the_behaviour_the_floor_lesson_depends_on_is_still_there(): + """Guards what the shim does, not only that its bytes are unchanged. + + A digest notices any edit but cannot tell a comment fix from someone + deleting the re-exec, which is the specific change that turns uvx nable + back into a dead end on an old interpreter. + """ + src = (SHIM / "nable_shim.py").read_text() + + assert "_reexec_under_managed_python" in src, ( + "the shim no longer re-execs under a managed interpreter, so uvx nable " + "on Python 3.10 is back to printing a command and exiting 1") + assert "KeyboardInterrupt" in src, ( + "Ctrl-C during the interpreter fetch prints the traceback this module " + "exists to prevent (PR #108)") From 5a879cb25c26ae951ee487be9951e2e828dfdfa0 Mon Sep 17 00:00:00 2001 From: chaandannn Date: Wed, 19 Aug 2026 15:38:15 -0500 Subject: [PATCH 2/2] fix(ci): the digest lock tripped the secret scanner detect-secrets flags any 64-char hex run as a Hex High Entropy String, which is the right default and wrong for a sha256 of a file anyone can read in this repo. Exemption is inline and covers that one line, not the file and not the rule. Verified locally with the same command CI runs: detect-secrets-hook --baseline .secrets.baseline Co-Authored-By: Claude Opus 5 --- tests/test_shim_ships_what_the_repo_has.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_shim_ships_what_the_repo_has.py b/tests/test_shim_ships_what_the_repo_has.py index c63df5a2..959e5650 100644 --- a/tests/test_shim_ships_what_the_repo_has.py +++ b/tests/test_shim_ships_what_the_repo_has.py @@ -40,7 +40,10 @@ # Bump BOTH when nable_shim.py changes, then push a shim-v tag. PUBLISHED_UNDER = "0.1.4" -SOURCE_SHA256 = "85a33fb6f03918f4fd43153d4f64e813faf13b15f09578e89d48d78c9c0e19a9" +# A digest of a file anyone can read in this repo, not a credential. The secret +# scanner flags any 64-char hex run as high entropy, which is the right default +# and wrong here, so the exemption is inline and covers this line only. +SOURCE_SHA256 = "85a33fb6f03918f4fd43153d4f64e813faf13b15f09578e89d48d78c9c0e19a9" # pragma: allowlist secret def _declared_version() -> str: