Skip to content

release: v4.17.0 + MCPB connector bundle in the release workflow - #334

Merged
cdeust merged 1 commit into
mainfrom
release/4.17.0
Aug 2, 2026
Merged

release: v4.17.0 + MCPB connector bundle in the release workflow#334
cdeust merged 1 commit into
mainfrom
release/4.17.0

Conversation

@cdeust

@cdeust cdeust commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Cuts v4.17.0 (116 commits since v4.16.0, 2026-07-24) and closes the gap that
made the MCPB connector unobtainable without cloning the repo.

Version surfaces

Nine sites carry the release identity and all move 4.16.0 → 4.17.0:
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 at 4.15.0 on purpose — it
is the migration shim, not a shipped version.

Gates run locally on the result, all exit 0:

Gate Result
scripts/check_marketplace_pins.py All marketplace pins current.
scripts/generate_repo_badges.py --check badges OK (4 checked)
scripts/check_doc_claims.py doc claims OK

CHANGELOG

[Unreleased] had accumulated ### Fixed and ### Security
across merges, unlike every published section. Versioned as [4.17.0] with
one sub-section per change type in Keep a Changelog order. Content-preserving
by construction and verified: the sorted multiset of body lines before/after
differs only by the added dependency-bump entry (58 → 59 lines).

MCPB bundle (new mcpb-bundle job)

manifest.json + .mcpbignore already described a valid MCPB 0.4 bundle, but
nothing built it — v4.14.3 through v4.16.0 shipped zero release assets.
The job packs the bundle on tag and:

  • fails if manifest.json's version does not match the tag
  • publishes a .sha256 companion
  • mints a Sigstore build-provenance attestation (gh attestation verify)
  • attaches a resolved server.json carrying both the pypi and mcpb
    package entries

The committed server.json keeps only the pypi package: the mcpb
registryType needs the asset URL and its digest, neither of which exists
before the build runs.

Toolchain is hash-pinned, not version-matched

Installed with npm ci against a committed lockfile rather than
npx @anthropic-ai/mcpb@2.1.2 — an exact version is not a pin (#203). The
lockfile pins 55 packages by sha512 integrity and overrides tmp to
0.2.7
, because the CLI's transitive @inquirer/promptsexternal-editor
tmp chain otherwise resolves inside the GHSA-52f5-9888-hmc6 /
GHSA-ph9p-34f9-6g65 range. npm audit reports 0 vulnerabilities after the
override.

.mcpbignore hardening

Adds deps/, fuzz/, .clusterfuzzlite/, requirements/. deps/ is the
git-ignored local pip --target tree: packing a working tree that had
bootstrapped the launcher produced 190.1MB / 26951 files where a clean
checkout produces 2.1MB / 561 files — a local pack and a CI pack now
agree. .gitignore also gains node_modules/, absent until now.

Verified locally before commit

npm ci + mcpb pack from a git archive tree yields
hypermnesia-mcp-4.17.0.mcpb (2.1MB, 561 files) containing manifest.json,
pyproject.toml, uv.lock, icon.png, README/LICENSE/PRIVACY and 536
mcp_server files, with zero files from tests_py, benchmarks,
deps, docs, fuzz, requirements, .github, scripts. The
resolved-server.json generator was executed against that bundle and its
fileSha256 matches sha256sum.

Not done in this PR

  • docs/ASSURANCE-CASE.md still reads _Last updated: 2026-07-27, against v4.16.0._ — bumping that line would assert a review of the assurance case
    against this tree that has not happened. Left for a deliberate pass.
  • The release-gate benchmarks (benchmarks/reproduce.sh, isolated container)
    have not been run. Per CLAUDE.md they are the only source of truth for
    a pre-tag floor decision.

🤖 Generated with Claude Code

Version surfaces moved 4.16.0 -> 4.17.0 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 — it is a migration shim,
not a shipped version. check_marketplace_pins.py, generate_repo_badges.py
--check, and check_doc_claims.py all exit 0 on the result.

CHANGELOG: the [Unreleased] block had accumulated 3x "### Fixed" and 2x
"### Security" across merges, unlike every published section. Versioned as
[4.17.0] with one sub-section per change type in Keep a Changelog order;
content-preserving (verified by diffing the sorted multiset of body lines
before and after — the only delta is the added dependency-bump entry).

MCPB bundle: manifest.json and .mcpbignore already described a valid MCPB
0.4 bundle, but no workflow built one, so v4.14.3..v4.16.0 shipped zero
release assets and the connector could only be produced by hand. The new
`mcpb-bundle` job packs it on tag, verifies the manifest version matches the
tag, publishes a sha256 companion, mints a Sigstore build-provenance
attestation, and attaches a resolved server.json carrying both the pypi and
the mcpb package entries — the mcpb entry needs the asset URL and its digest,
neither of which exists before the build, which is why the committed
server.json keeps only the version-addressable pypi package.

The CLI is installed with `npm ci` against a committed lockfile rather than
`npx @anthropic-ai/mcpb@2.1.2`: an exact version is not a pin (issue #203).
The lockfile pins 55 packages by sha512 integrity and overrides `tmp` to
0.2.7, because the CLI's transitive @inquirer/prompts -> external-editor ->
tmp chain otherwise resolves inside the GHSA-52f5-9888-hmc6 /
GHSA-ph9p-34f9-6g65 range; `npm audit` reports 0 vulnerabilities after the
override.

.mcpbignore additionally excludes deps/, fuzz/, .clusterfuzzlite/ and
requirements/. deps/ is the git-ignored local pip --target tree: packing a
working tree that had bootstrapped the launcher produced 190.1MB / 26951
files where a clean checkout produces 2.1MB / 561 files, so a local pack and
a CI pack now agree. .gitignore gains node_modules/, absent until now.

Verified locally before commit: npm ci + `mcpb pack` from a `git archive`
tree yields hypermnesia-mcp-4.17.0.mcpb (2.1MB, 561 files) containing
manifest.json, pyproject.toml, uv.lock, icon.png, README/LICENSE/PRIVACY and
536 mcp_server files, with zero files from tests_py, benchmarks, deps, docs,
fuzz, requirements, .github or scripts; the resolved-server.json generator
was executed against that bundle and its fileSha256 matches sha256sum.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cdeust
cdeust merged commit 13278df into main Aug 2, 2026
20 checks passed
@cdeust
cdeust deleted the release/4.17.0 branch August 2, 2026 09:19
cdeust added a commit that referenced this pull request Aug 2, 2026
…hip it as v4.17.1 (#335)

* ci(release): mirror ci.yml network hardening in the release test gate

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>

* release: v4.17.1 — ship v4.17.0's tree under a version whose workflow 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>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant