From e125d51489b0f470459ed21f19164d3132b40ef6 Mon Sep 17 00:00:00 2001 From: ericwalisko Date: Thu, 3 Sep 2026 19:59:47 -0400 Subject: [PATCH] Bump plugin manifest version and guard it against drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Claude Code plugin manifest version sat at 0.1.1 across every release from 0.1.2 onward. `claude plugin update` only reinstalls when the manifest version rises, so it compared 0.1.1 to 0.1.1 and did nothing — no plugin or hook change (e.g. the 0.2.x automatic session-end backup hook, already present in hooks.json) could ever reach an installed machine. The CLI version is CI-checked against the git tag, but the plugin manifest was a separate string with nothing watching it. Bump both manifests (plugin.json and marketplace.json) to the package version, and add tests/test_plugin_manifest.py asserting they equal __version__ so a release that forgets the plugin bump fails CI. Document the three-place bump in RELEASING.md. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01NnaQwZUSDRbVUNh8fsdksD --- .claude-plugin/marketplace.json | 2 +- .gitignore | 3 +++ CHANGELOG.md | 7 ++++++ RELEASING.md | 14 +++++++++-- .../claude-code/.claude-plugin/plugin.json | 2 +- tests/test_plugin_manifest.py | 24 +++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 tests/test_plugin_manifest.py diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 88d47f7..79a9d04 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "name": "memware", "source": "./integrations/claude-code", "description": "Session transcripts indexed for recall; a belief ledger that only remembers the latest truth. Hooks: SessionEnd/PreCompact sync, prompt-time belief context.", - "version": "0.1.1", + "version": "0.2.4", "author": { "name": "ericwalisko" } diff --git a/.gitignore b/.gitignore index 61afea0..0e91f23 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ htmlcov/ *.db-wal *.db-shm .memware/ + +# uv lockfile (library, not committed) +uv.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 9169362..2661188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project are documented here. The format follows ## [Unreleased] +### Fixed +- The Claude Code plugin manifest version was stuck at 0.1.1 across every release, so + `claude plugin update` compared 0.1.1 to 0.1.1 and never reinstalled — no plugin or hook + change (e.g. the 0.2.x automatic session-end backup hook) could reach an installed + machine. Both manifests now track the package version, enforced by + `tests/test_plugin_manifest.py` so it cannot silently drift again. + ## [0.2.4] - 2026-09-03 ### Fixed diff --git a/RELEASING.md b/RELEASING.md index 19c33f6..e29573d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -71,13 +71,23 @@ Troubleshooting below. ## Cutting a release -1. **Bump the version.** It lives in exactly one place: +1. **Bump the version.** The package version lives in: ``` src/memware/__init__.py → __version__ = "0.1.1" ``` - `pyproject.toml` reads it via `[tool.hatch.version]`, so nothing else needs editing. + `pyproject.toml` reads it via `[tool.hatch.version]`. The Claude Code plugin also carries + its own copy of the version in two manifests, and `tests/test_plugin_manifest.py` fails CI + unless both equal the package version — so bump all three together: + + ``` + integrations/claude-code/.claude-plugin/plugin.json → "version" + .claude-plugin/marketplace.json → plugins[memware].version + ``` + + `claude plugin update` only reinstalls when the manifest version rises, so a plugin or hook + change that skips this bump never reaches an installed machine. 2. **Cut the CHANGELOG.** Move the `## [Unreleased]` entries under a new `## [X.Y.Z] - YYYY-MM-DD` heading and leave `## [Unreleased]` empty above it. diff --git a/integrations/claude-code/.claude-plugin/plugin.json b/integrations/claude-code/.claude-plugin/plugin.json index 47380a1..3a07fe5 100644 --- a/integrations/claude-code/.claude-plugin/plugin.json +++ b/integrations/claude-code/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "memware", "description": "Session transcripts indexed for recall; a belief ledger that only remembers the latest truth.", - "version": "0.1.1", + "version": "0.2.4", "author": { "name": "ericwalisko" } diff --git a/tests/test_plugin_manifest.py b/tests/test_plugin_manifest.py new file mode 100644 index 0000000..7cf8e9f --- /dev/null +++ b/tests/test_plugin_manifest.py @@ -0,0 +1,24 @@ +"""The Claude Code plugin carries its own version string, and `claude plugin update` only +reinstalls when it rises. Nothing in the release path enforced it, so it silently sat at 0.1.1 +across many releases and no hook change ever reached an installed machine. These tests keep both +manifests equal to the package version, so cutting a release always moves the plugin too.""" + +import json +from pathlib import Path + +import memware + +ROOT = Path(__file__).resolve().parents[1] +PLUGIN = ROOT / "integrations" / "claude-code" / ".claude-plugin" / "plugin.json" +MARKETPLACE = ROOT / ".claude-plugin" / "marketplace.json" + + +def test_plugin_manifest_version_matches_package(): + plugin = json.loads(PLUGIN.read_text()) + assert plugin["version"] == memware.__version__, (plugin["version"], memware.__version__) + + +def test_marketplace_plugin_version_matches_package(): + market = json.loads(MARKETPLACE.read_text()) + entry = next(p for p in market["plugins"] if p["name"] == "memware") + assert entry["version"] == memware.__version__, (entry["version"], memware.__version__)