diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a2056ea..42cd3cd 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "name": "terraphim-skills-intro", "source": "./", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "version": "0.2.1", + "version": "0.2.2", "category": "development", "homepage": "https://terraphim-skills.md/" } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 1442ca0..81adc68 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "terraphim-skills-intro", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "version": "0.2.1", + "version": "0.2.2", "author": { "name": "Terraphim AI", "email": "engineering@terraphim.ai" diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 062a49f..6cd46af 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "terraphim-skills-intro", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "version": "0.2.1", + "version": "0.2.2", "author": { "name": "Terraphim AI", "email": "engineering@terraphim.ai" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 105cc63..f52a270 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,10 +29,17 @@ jobs: run: | expected_tag="v$(python3 scripts/generate_distribution.py --print-version)" test "$GITHUB_REF_NAME" = "$expected_tag" + # actions/checkout can leave the event tag name pointing at the + # peeled commit. Fetch the remote annotated tag into a private ref so + # signature verification always targets the tag object itself. + release_ref="refs/release-tags/$GITHUB_REF_NAME" + git fetch --no-tags --force origin \ + "refs/tags/$GITHUB_REF_NAME:$release_ref" + test "$(git rev-list -n 1 "$release_ref")" = "$GITHUB_SHA" git config gpg.format ssh git config gpg.ssh.allowedSignersFile \ "$GITHUB_WORKSPACE/.github/trusted-release-signers" - git verify-tag "$GITHUB_REF_NAME" + git verify-tag "$release_ref" python3 scripts/generate_distribution.py --check python3 -m unittest discover -s tests -p 'test_*.py' @@ -57,6 +64,17 @@ jobs: with: persist-credentials: false + - name: Re-verify immutable release tag + run: | + release_ref="refs/release-tags/$GITHUB_REF_NAME" + git fetch --no-tags --force origin \ + "refs/tags/$GITHUB_REF_NAME:$release_ref" + test "$(git rev-list -n 1 "$release_ref")" = "$GITHUB_SHA" + git config gpg.format ssh + git config gpg.ssh.allowedSignersFile \ + "$GITHUB_WORKSPACE/.github/trusted-release-signers" + git verify-tag "$release_ref" + - name: Publish immutable release assets env: GH_TOKEN: ${{ github.token }} diff --git a/.zcode-plugin/plugin.json b/.zcode-plugin/plugin.json index 956e3c6..5e4adda 100644 --- a/.zcode-plugin/plugin.json +++ b/.zcode-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "terraphim-skills-intro", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "version": "0.2.1", + "version": "0.2.2", "author": { "name": "Terraphim AI", "email": "engineering@terraphim.ai" diff --git a/dist/autoclaw/SHA256SUMS b/dist/autoclaw/SHA256SUMS index 020f43c..59c9131 100644 --- a/dist/autoclaw/SHA256SUMS +++ b/dist/autoclaw/SHA256SUMS @@ -1,3 +1,3 @@ -38d8f394d04aeec419cc52a1d146525b8357bc48df5b03b0f24970e5b37453d9 terraphim-grep-0.2.1.zip -69c2be81b8ff879fc7f9a62f5d48399be98e9a9934f3b17e2fcd657729590012 terraphim-agent-learn-0.2.1.zip -b0f9de6fa5eff647b92bd6a6aeabe825ed3f7e00d1f690ae4f0145a7489357e9 terraphim-agent-memory-0.2.1.zip +38d8f394d04aeec419cc52a1d146525b8357bc48df5b03b0f24970e5b37453d9 terraphim-grep-0.2.2.zip +69c2be81b8ff879fc7f9a62f5d48399be98e9a9934f3b17e2fcd657729590012 terraphim-agent-learn-0.2.2.zip +b0f9de6fa5eff647b92bd6a6aeabe825ed3f7e00d1f690ae4f0145a7489357e9 terraphim-agent-memory-0.2.2.zip diff --git a/dist/autoclaw/terraphim-agent-learn-0.2.1.zip b/dist/autoclaw/terraphim-agent-learn-0.2.2.zip similarity index 100% rename from dist/autoclaw/terraphim-agent-learn-0.2.1.zip rename to dist/autoclaw/terraphim-agent-learn-0.2.2.zip diff --git a/dist/autoclaw/terraphim-agent-memory-0.2.1.zip b/dist/autoclaw/terraphim-agent-memory-0.2.2.zip similarity index 100% rename from dist/autoclaw/terraphim-agent-memory-0.2.1.zip rename to dist/autoclaw/terraphim-agent-memory-0.2.2.zip diff --git a/dist/autoclaw/terraphim-grep-0.2.1.zip b/dist/autoclaw/terraphim-grep-0.2.2.zip similarity index 100% rename from dist/autoclaw/terraphim-grep-0.2.1.zip rename to dist/autoclaw/terraphim-grep-0.2.2.zip diff --git a/distribution/metadata.json b/distribution/metadata.json index e9e1a80..5e2276c 100644 --- a/distribution/metadata.json +++ b/distribution/metadata.json @@ -3,7 +3,7 @@ "plugin": { "name": "terraphim-skills-intro", "display_name": "Terraphim Skills Introduction", - "version": "0.2.1", + "version": "0.2.2", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", "description_zh_cn": "使用 Terraphim 命令行工具搜索代码、记录经验并检索记忆。", "author_name": "Terraphim AI", diff --git a/docs/distribution.md b/docs/distribution.md index 0f800ff..9ef5270 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -1,6 +1,6 @@ # Multi-marketplace distribution -This repository is the canonical source for all hosts. Release `v0.2.1` contains +This repository is the canonical source for all hosts. Release `v0.2.2` contains exactly three skills and deterministic AutoClaw archives. ## Availability model @@ -13,11 +13,18 @@ exactly three skills and deterministic AutoClaw archives. - AutoClaw local ZIP import is supported. ZhipuAI Skills Center inclusion is a separate external review; ClawHub is not treated as proof of that inclusion. +## Release integrity + +Release tags match `v*`, are SSH-signed by a checked-in trusted signer, and are +verified against the exact GitHub event commit before assets are published. The +repository's active tag ruleset must restrict updates and deletions for `v*` so +the tag cannot move between verification and release publication. + ## Kimi Code Install the immutable release URL through Kimi Code's `/plugins install`, or open `/plugins marketplace` with -`https://raw.githubusercontent.com/terraphim/terraphim-cursor-plugin/v0.2.1/marketplaces/kimi.json` +`https://raw.githubusercontent.com/terraphim/terraphim-cursor-plugin/v0.2.2/marketplaces/kimi.json` and install `terraphim-skills-intro`. Run `/plugins reload` (or start a new session), then confirm that exactly the three documented skills appear. The native manifest is @@ -27,7 +34,7 @@ Update by installing the newer tag; remove through Kimi's plugin manager. ## AutoClaw -Download the three ZIP files and `SHA256SUMS` from the `v0.2.1` release. Verify +Download the three ZIP files and `SHA256SUMS` from the `v0.2.2` release. Verify each digest, then use **Skills & Connectors → Skills → Create → Add skill file**. Import each ZIP separately. Restart AutoClaw and confirm the skills persist. Remove them through the same Skills screen. If Homebrew binaries are missing from the GUI @@ -36,7 +43,7 @@ them through the same Skills screen. If Homebrew binaries are missing from the G ## ZCode Open ZCode's **Discover** view and add -`https://raw.githubusercontent.com/terraphim/terraphim-cursor-plugin/v0.2.1/marketplaces/zcode.json` +`https://raw.githubusercontent.com/terraphim/terraphim-cursor-plugin/v0.2.2/marketplaces/zcode.json` as a personal marketplace source, install `terraphim-skills-intro`, then confirm the Plugin Skills view shows exactly three skills. Curated discovery requires a separate pull request to `zai-org/zcode-plugins`; this repository does not claim that @@ -54,7 +61,7 @@ hermes skills inspect terraphim/terraphim-cursor-plugin/skills/terraphim-agent-l hermes skills inspect terraphim/terraphim-cursor-plugin/skills/terraphim-agent-memory ``` -Record the immutable `v0.2.1` provenance. Remove the skills with Hermes' normal +Record the immutable `v0.2.2` provenance. Remove the skills with Hermes' normal skill-management command. ## skills.sh @@ -66,7 +73,7 @@ npx skills add terraphim/terraphim-cursor-plugin ``` Set `DISABLE_TELEMETRY=1` to opt out of anonymous telemetry. Confirm exactly three -skills are offered and that their files match tag `v0.2.1`. Update by repeating the +skills are offered and that their files match tag `v0.2.2`. Update by repeating the command for the new tag; remove the installed skill directories through the target agent's documented skill manager. diff --git a/docs/uat/evidence-v0.2.2.md b/docs/uat/evidence-v0.2.2.md new file mode 100644 index 0000000..b21602e --- /dev/null +++ b/docs/uat/evidence-v0.2.2.md @@ -0,0 +1,32 @@ +# Validation evidence — v0.2.2 + +## Purpose + +Patch release `v0.2.2` preserves the reviewed Community pack and makes release +signature verification independent of the tag reference created by +`actions/checkout`. + +## Regression addressed + +The `v0.2.1` workflow fetched complete history, but GitHub Actions still left +the event's local tag name pointing at the peeled commit. The remote +`refs/tags/v0.2.1` remained an intact signed annotated tag. Validation now +fetches the remote tag into `refs/release-tags/`, proves its peeled +commit equals `GITHUB_SHA`, and verifies the signature on that private ref in +both the read-only validation job and the write-scoped publication job. + +The repository must also have an active tag ruleset for `v*` that restricts tag +updates and deletions without granting routine bypass. This closes the remaining +time-of-check/time-of-use window between re-verification and GitHub's release +creation API. Release publication must not proceed without that operational +control. + +## Required evidence + +- generator drift and all distribution tests pass; +- Ruff, actionlint, Claude strict validation, and `git diff --check` pass; +- AutoClaw archives reproduce byte-for-byte and their checksums match; +- the signed `v0.2.2` tag verifies against `.github/trusted-release-signers`; +- the active `v*` tag ruleset restricts updates and deletions; +- GitHub release validation verifies the private tag ref and publishes the six + expected assets. diff --git a/docs/uat/multi-marketplace.md b/docs/uat/multi-marketplace.md index ac0f038..d5dd722 100644 --- a/docs/uat/multi-marketplace.md +++ b/docs/uat/multi-marketplace.md @@ -8,7 +8,7 @@ older installation cannot make a failed package appear healthy. 1. Install and verify the runtime dependencies using [Install dependencies](../install-dependencies.md). -2. Confirm the plugin version is `0.2.1`. +2. Confirm the plugin version is `0.2.2`. 3. Confirm the installed skill set is exactly: `terraphim-grep`, `terraphim-agent-learn`, and `terraphim-agent-memory`. diff --git a/kimi.plugin.json b/kimi.plugin.json index 599551b..c044a08 100644 --- a/kimi.plugin.json +++ b/kimi.plugin.json @@ -1,7 +1,7 @@ { "name": "terraphim-skills-intro", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "version": "0.2.1", + "version": "0.2.2", "author": { "name": "Terraphim AI", "email": "engineering@terraphim.ai" diff --git a/marketplaces/kimi.json b/marketplaces/kimi.json index 31ab951..727bbbd 100644 --- a/marketplaces/kimi.json +++ b/marketplaces/kimi.json @@ -5,7 +5,7 @@ "id": "terraphim-skills-intro", "displayName": "Terraphim Skills Introduction", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "source": "https://github.com/terraphim/terraphim-cursor-plugin/archive/refs/tags/v0.2.1.zip" + "source": "https://github.com/terraphim/terraphim-cursor-plugin/archive/refs/tags/v0.2.2.zip" } ] } diff --git a/marketplaces/zcode.json b/marketplaces/zcode.json index 8caa9b6..bb18bc6 100644 --- a/marketplaces/zcode.json +++ b/marketplaces/zcode.json @@ -8,14 +8,14 @@ "source": "github", "repo": "terraphim/terraphim-cursor-plugin", "path": ".", - "ref": "v0.2.1" + "ref": "v0.2.2" }, "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", "description_i18n": { "en": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", "zh-CN": "使用 Terraphim 命令行工具搜索代码、记录经验并检索记忆。" }, - "version": "0.2.1", + "version": "0.2.2", "category": "developer-tools", "tags": [ "terraphim", diff --git a/plugin.json b/plugin.json index 599551b..c044a08 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "name": "terraphim-skills-intro", "description": "Search code, capture learning, and retrieve memory with Terraphim command-line tools.", - "version": "0.2.1", + "version": "0.2.2", "author": { "name": "Terraphim AI", "email": "engineering@terraphim.ai" diff --git a/tests/test_distribution.py b/tests/test_distribution.py index 99a36b7..9fad5bb 100644 --- a/tests/test_distribution.py +++ b/tests/test_distribution.py @@ -31,11 +31,11 @@ def test_exact_public_skill_allowlist_and_tree(self) -> None: self.assertEqual(GENERATOR.ALLOWED_SKILLS, names) def test_print_version_matches_manifests(self) -> None: - self.assertEqual("0.2.1", self.metadata["plugin"]["version"]) + self.assertEqual("0.2.2", self.metadata["plugin"]["version"]) output = io.StringIO() with redirect_stdout(output): self.assertEqual(0, GENERATOR.main(["--print-version"])) - self.assertEqual("0.2.1\n", output.getvalue()) + self.assertEqual("0.2.2\n", output.getvalue()) def test_generated_manifests_are_current_and_deterministic(self) -> None: expected = GENERATOR.generated_files(ROOT, self.metadata) @@ -93,7 +93,7 @@ def test_zcode_marketplace_pins_release_and_category(self) -> None: entry = marketplace["plugins"][0] self.assertEqual("terraphim-skills-intro", entry["name"]) self.assertEqual("developer-tools", entry["category"]) - self.assertEqual("v0.2.1", entry["source"]["ref"]) + self.assertEqual("v0.2.2", entry["source"]["ref"]) self.assertTrue(entry["strict"]) self.assertEqual({"en", "zh-CN"}, set(entry["description_i18n"])) manifest = json.loads((ROOT / ".zcode-plugin/plugin.json").read_text()) @@ -136,7 +136,16 @@ def test_committed_autoclaw_checksums_match_archives(self) -> None: def test_release_checkout_fetches_annotated_tag_object(self) -> None: workflow = (ROOT / ".github/workflows/release.yml").read_text() self.assertIn("fetch-depth: 0", workflow) - self.assertIn('git verify-tag "$GITHUB_REF_NAME"', workflow) + self.assertIn('release_ref="refs/release-tags/$GITHUB_REF_NAME"', workflow) + self.assertIn('"refs/tags/$GITHUB_REF_NAME:$release_ref"', workflow) + self.assertEqual( + 2, + workflow.count( + 'test "$(git rev-list -n 1 "$release_ref")" = "$GITHUB_SHA"' + ), + ) + self.assertIn('git verify-tag "$release_ref"', workflow) + self.assertEqual(2, workflow.count('git verify-tag "$release_ref"')) def test_docs_cover_hosts_and_dependency_probes(self) -> None: readme = (ROOT / "README.md").read_text().lower()