ci(release): mirror ci.yml's network hardening in the release gate, ship it as v4.17.1 - #335
Merged
Merged
Conversation
Tag pushes run `release.yml`'s own `test` job, but `ci.yml` triggers on `push: branches: [main]` + `pull_request` (ci.yml:4-8), so no tag has ever reached it. Every hardening pass CI absorbed since 2026-07-27 therefore skipped this file, and the two jobs silently diverged while running the same suite. Release run 30741657854 (v4.17.0) is what that divergence cost: test_spell_alteration.py::test_recall_real_spell_by_name -> pg_recall.py:443 -> reranker.py:109 -> FlashRank's bare `requests.get(..., stream=True)`, which carries no timeout, so a stalled connect hung in `sock.connect` until pytest-timeout killed the suite. HF_HUB_OFFLINE does not reach FlashRank's own fetch path, and reranker.py's `except Exception` cannot engage against a hang that never raises. All five downstream publish jobs were blocked on a tag whose tree had just passed 20 green checks on PR #334; the release shipped zero artifacts. Ported one-for-one from ci.yml:103-115,142-195 — cache + retried prefetch + offline test run, per model: - cache `~/.cache/flashrank`, matching reranker_model.py:104-113's `reranker_cache_dir()` (which honours $XDG_CACHE_HOME); - prefetch the reranker via `ensure_reranker_loaded()` and assert `state == 'loaded'`, so a failed fetch fails the step instead of surfacing later as first-stage-only recall scores (the 2026-07-10 FlashRank incident); - harden the HF prefetch to 5 retries with backoff and drop `continue-on-error`, so a blip cannot leave the cache empty and cascade into a misleading test failure; - run pytest with HF_HUB_OFFLINE / TRANSFORMERS_OFFLINE / CORTEX_RERANKER_OFFLINE, so no model download can happen mid-suite. ci.yml's three tree-sitter steps are deliberately NOT ported: requirements/ release.txt omits tree-sitter and tree-sitter-language-pack (as it omits igraph, leidenalg and texttable), so the AST tests skip in this job and there is no grammar to fetch — porting them would have failed on ImportError. A comment records the corollary: this gate tests a narrower surface than CI, and the three steps must follow if release.txt ever gains that dependency. Verified locally in .venv: `ensure_reranker_loaded()` returns state='loaded'. actionlint is not installed on this machine, so workflow validation was limited to a YAML parse plus review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… works v4.17.0 was tagged but published nothing: its `test` job hung, blocking all five downstream publish jobs, so no GitHub release, no PyPI upload and no .mcpb bundle exist for it. The cause and its fix are the preceding commit. A new patch version rather than a moved tag, because a tag executes the `release.yml` of ITS OWN tree: `v4.17.0` points at 13278df, which carries the unhardened workflow, so a re-run would replay the same hang; and recreating a published tag would break the "tree bit-identical to ae633a8" property the v4.17.0 release decision rests on. House precedent: v3.15.2 abandoned -> v3.15.3. v4.17.1 therefore carries v4.17.0's tree plus the workflow fix — no source change. Version surfaces moved 4.17.0 -> 4.17.1 across the nine sites that carry the release identity: pyproject.toml, server.json (document + pypi package), manifest.json, .claude-plugin/plugin.json, .claude-plugin/marketplace.json (metadata.version + the hypermnesia-mcp entry), uv.lock's root package, package.json, and the generated assets/badge-version.svg. The deprecated `cortex` marketplace entry stays pinned at 4.15.0 — a migration shim, not a shipped version. README's badge alt text, docs/ROADMAP.md's "where the project is today" line, and .bestpractices.json's three version justifications follow. manifest.json matters beyond bookkeeping here: the mcpb-bundle job refuses to pack when its version does not equal the tag (release.yml:419-421), so this is the file the v4.17.1 tag will be checked against. Gates: generate_repo_badges.py --check (4 badges), check_doc_claims.py, and check_marketplace_pins.py all exit 0; every touched JSON parses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
v4.17.0 is tagged but published nothing. Run 30741657854 failed in the
testjob and blocked all five downstream publish jobs — no GitHub release, no PyPI upload, no.mcpbbundle exists for that tag.The cause is a trigger asymmetry, not a flake:
A tag push never reaches
ci.yml. So every network-hardening pass CI absorbed since 2026-07-27 silently skippedrelease.yml, while both files kept running the same suite. The two jobs diverged with nothing to report it.What that cost:
HF_HUB_OFFLINEdoes not reach FlashRank's own fetch path, andreranker.py'sexcept Exceptioncannot engage against a hang that never raises. The tree had just passed 20 green checks on #334.What changed
Commit 1 —
release.yml'stestjob mirrors ci.yml:103-115,142-195 one-for-one (cache + retried prefetch + offline run, per model):~/.cache/flashrank, matchingreranker_model.py:104-113'sreranker_cache_dir()(which honours$XDG_CACHE_HOME);ensure_reranker_loaded()assertingstate == 'loaded'— a failed fetch fails the step instead of resurfacing later as first-stage-only recall scores (the 2026-07-10 FlashRank incident);continue-on-error— a blip must not leave the cache empty and cascade into a misleading test failure;HF_HUB_OFFLINE/TRANSFORMERS_OFFLINE/CORTEX_RERANKER_OFFLINE, so no download can happen mid-suite.ci.yml's three tree-sitter steps are deliberately NOT ported.
requirements/release.txtomits tree-sitter and tree-sitter-language-pack (as it omits igraph, leidenalg, texttable), so the AST tests skip in this job and there is no grammar to fetch — porting them would have failed onImportError. A comment records the corollary: this gate tests a narrower surface than CI, and the three steps must follow ifrelease.txtever gains that dependency.Commit 2 — bump to v4.17.1. No source change; v4.17.1 carries v4.17.0's tree plus the workflow fix.
Why a new version rather than moving the tag
A tag executes the
release.ymlof its own tree.v4.17.0points at13278df4, which carries the unhardened file — a re-run would replay the same hang. Recreating the tag would rewrite a published ref and break the "tree bit-identical toae633a87" property the v4.17.0 release decision rests on. House precedent: v3.15.2 abandoned → v3.15.3.workflow_dispatchwas not an option either: the publish jobs gate onevent_name == 'push'.Verification
ensure_reranker_loaded()→state='loaded'in a local.venv;generate_repo_badges.py --check(4 badges),check_doc_claims.py,check_marketplace_pins.py— all exit 0;actionlintis not installed on this machine, so workflow validation was limited to a YAML parse plus review. CI on this PR is the real check.Follow-up (not in this PR)
The durable fix for the divergence is a shared
workflow_callbetweenci.ymlandrelease.yml. Set aside today in favour of alignment; issue to follow.🤖 Generated with Claude Code