From 42b37ef0052db6e700fa0daaa4168fc018c06a69 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Tue, 8 Sep 2026 17:42:42 +0200 Subject: [PATCH 1/2] fix(marketplace): re-sync magpie-agent-guard with the dev version stamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `#1169` stamped the framework version to `0.2.0.dev202609081000` across every ecosystem manifest. `#1170` landed right after it, adding the `magpie-agent-guard` plugin — a *new* file, branched before the bump and therefore carrying the previous stamp `0.2.0.dev202609080121`. New files do not conflict, so both merged cleanly and `main` was left with a family plugin one stamp behind the root manifest it inherits from. `check-family-plugins` has failed on `main` ever since, which is why an unrelated dependabot PR (#1167) shows a red `prek` job it did not cause. `python3 tools/dev/check-family-plugins.py --fix` output; no hand edits. Generated-by: Claude Code (Opus 5) --- .claude-plugin/marketplace.json | 2 +- plugins/magpie-agent-guard/.claude-plugin/plugin.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 39b0c1385..1bce7192c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -15,7 +15,7 @@ { "name": "magpie-agent-guard", "source": "./plugins/magpie-agent-guard", - "version": "0.2.0.dev202609080121", + "version": "0.2.0.dev202609081000", "description": "Apache Magpie \u2014 deterministic pre-execution command guard: a PreToolUse hook that denies shell commands which would break a hard framework rule. Runs from the installed plugin, so no repository or worktree needs a local copy." }, { diff --git a/plugins/magpie-agent-guard/.claude-plugin/plugin.json b/plugins/magpie-agent-guard/.claude-plugin/plugin.json index 36ee101e1..40dd2debd 100644 --- a/plugins/magpie-agent-guard/.claude-plugin/plugin.json +++ b/plugins/magpie-agent-guard/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "magpie-agent-guard", "description": "Apache Magpie \u2014 deterministic pre-execution command guard: a PreToolUse hook that denies shell commands which would break a hard framework rule. Runs from the installed plugin, so no repository or worktree needs a local copy.", - "version": "0.2.0.dev202609080121", + "version": "0.2.0.dev202609081000", "author": { "name": "Apache Magpie", "url": "https://magpie.apache.org/" From 93bc3f4e09f465f572e062a44df64eaa724c4524 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Tue, 8 Sep 2026 17:47:41 +0200 Subject: [PATCH 2/2] fix(prek): regenerate the family plugins instead of reporting them stale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `check-family-plugins` hook ran check-only, so a contributor whose manifests drifted got an instruction — "run `tools/dev/check-family-plugins.py --fix`" — rather than a fixed tree. The generated files are a pure function of the skills' `family:` frontmatter and `pyproject.toml`'s version, so there is nothing for them to decide: the fixer is the only correct response to every failure the check can report. Run the hook as `--fix`. prek fails a run whose hook modified files, so drift still stops the commit; it now arrives already corrected, needing only a re-stage — the same convention `end-of-file-fixer`, `trailing-whitespace`, and `add-license-headers` already follow here. `--fix`'s exit code is deliberately left alone: the release-bump procedure invokes it directly in plain shell blocks, where returning non-zero on a successful regeneration would read as failure. Scope note: this closes the hand-fix gap for anyone who runs the hooks, but it cannot catch the cross-PR case that broke `main` this morning — two PRs green in isolation, conflict-free on merge, out of sync in the result. Only a check on `main` (or a merge queue) sees that. Docs describing the hook as check-only updated to match. Generated-by: Claude Code (Opus 5) --- .pre-commit-config.yaml | 11 +++++++++-- docs/setup/marketplaces.md | 5 +++-- projects/magpie/release-management-config.md | 5 +++-- tools/dev/README.md | 2 +- tools/spec-loop/specs/marketplace-distribution.md | 4 +++- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9000282e7..c27ca0654 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -259,13 +259,20 @@ repos: # `.claude-plugin/marketplace.json` entry resolving to a matching, uniquely # named plugin.json, and each family's skills present as single-hop symlinks # into the shared skills/ tree. Catches a new skill, a changed family, a stale - # symlink, or a malformed manifest. `--fix` regenerates the family plugins. + # symlink, or a malformed manifest. + # + # Runs as `--fix`, so the hook *regenerates* rather than merely reporting: the + # generated tree is a pure function of the frontmatter and `pyproject.toml`'s + # version, so there is nothing for a contributor to decide when it drifts, and + # a check-only hook just made them run the fixer by hand. prek fails the run + # when a hook modifies files, so drift still stops the commit — it now arrives + # already corrected and staged-ready instead of as an instruction. - repo: local hooks: - id: check-family-plugins name: check-family-plugins (marketplace plugins vs frontmatter) language: system - entry: python3 tools/dev/check-family-plugins.py + entry: python3 tools/dev/check-family-plugins.py --fix files: ^(skills/.*/SKILL\.md|plugins/.*|\.claude-plugin/(marketplace|plugin)\.json|\.codex-plugin/plugin\.json|\.agents/plugins/marketplace\.json|(plugin|marketplace)\.json|gemini-extension\.json|apm\.yml|pyproject\.toml|hooks/check-upgrade\.sh)$ pass_filenames: false # Documentation claims that must track the tree. Four mechanical checks, diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index cbb577e4d..f02885b1e 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -486,8 +486,9 @@ and the all-in-one [`.claude-plugin/plugin.json`](../../.claude-plugin/plugin.js in turn feeds the ten per-family manifests and the marketplace entries, which also inherit `author`, `homepage`, `repository`, and `license`. Bump `project.version` and run `python3 tools/dev/check-family-plugins.py --fix`; the -same script, run as a prek hook, fails the build on any manifest left behind at -the old version. See +same script runs as a prek hook in `--fix` mode, so a manifest left behind at +the old version is regenerated in place and the run fails until the corrected +file is staged. See [`release-management-config.md`](../../projects/magpie/release-management-config.md) (`version_manifest_files`). diff --git a/projects/magpie/release-management-config.md b/projects/magpie/release-management-config.md index a563466b8..05558c8ee 100644 --- a/projects/magpie/release-management-config.md +++ b/projects/magpie/release-management-config.md @@ -122,8 +122,9 @@ So a bump is one edit plus one command: python3 tools/dev/check-family-plugins.py --fix ``` -`tools/dev/check-family-plugins.py` (a prek hook) fails the build if any -manifest or marketplace entry is left behind at the previous version, so a +`tools/dev/check-family-plugins.py` also runs as a prek hook in `--fix` mode: +any manifest or marketplace entry left behind at the previous version is +regenerated in place and the run fails until the corrected file is staged, so a missed propagation cannot reach a release. ## Backends diff --git a/tools/dev/README.md b/tools/dev/README.md index 1ddaf7f58..8aab01232 100644 --- a/tools/dev/README.md +++ b/tools/dev/README.md @@ -46,7 +46,7 @@ installable for other members to depend on it. | Script | What it does | |---|---| | [`check-doc-sync.py`](check-doc-sync.py) | Guards the documentation claims that track the tree and rot silently: spec-index completeness (every `tools/spec-loop/specs/*.md` listed in **both** `overview.md` and `README.md`), the per-family skill counts in the root `README.md`, the per-mode counts in `docs/modes.md`'s *Modes at a glance* table, the bare catalogue totals in `docs/setup/marketplaces.md`, and that every script here is named in this file. | -| [`check-family-plugins.py`](check-family-plugins.py) | Validates the marketplace plugins against the skills' `family:` frontmatter — version parity across every ecosystem manifest, Agent Plugins 1.0 conformance, and one well-formed per-family plugin whose `skills/` symlinks match the family exactly. `--fix` regenerates them. | +| [`check-family-plugins.py`](check-family-plugins.py) | Validates the marketplace plugins against the skills' `family:` frontmatter — version parity across every ecosystem manifest, Agent Plugins 1.0 conformance, and one well-formed per-family plugin whose `skills/` symlinks match the family exactly. `--fix` regenerates them, which is how the prek hook runs it. | | [`check-placeholders.sh`](check-placeholders.sh) | Fails the build on hardcoded project references in skill and tool docs, which must use `` / `` / `` / `` instead. Carries both casings and matches spaced variants. | | [`check-workspace-members.py`](check-workspace-members.py) | Catches a new `tools//pyproject.toml` that was never added to `[tool.uv.workspace] members` — an omission that silently drops the tool from both the pre-commit hooks and the CI pytest matrix. Also verifies each member's tests actually run: both surfaces key off `[tool.pytest.ini_options]`, so a project can carry a full `tests/` directory and be executed by nothing. Reports tests-without-config, config-without-tests, and neither; `[tool.magpie.checks] skip = ["pytest"]` is the declared exemption. | | [`run-workspace-check.sh`](run-workspace-check.sh) | Runs one static-check or test command across every workspace member, auto-discovering which members a given check applies to. The four `workspace-*` hooks call it, so adding a tool needs no edit to the pre-commit config. | diff --git a/tools/spec-loop/specs/marketplace-distribution.md b/tools/spec-loop/specs/marketplace-distribution.md index 0286cb955..fcd65d69f 100644 --- a/tools/spec-loop/specs/marketplace-distribution.md +++ b/tools/spec-loop/specs/marketplace-distribution.md @@ -53,7 +53,9 @@ family, each a manifest and a `skills/` directory of single-hop symlinks into the shared `skills/` tree. `tools/dev/check-family-plugins.py` is both the generator (`--fix`) and the -CI gate. `docs/setup/marketplaces.md` is the adopter-facing page. +CI gate — the prek hook runs it in `--fix` mode, so the gate corrects drift +rather than only reporting it. `docs/setup/marketplaces.md` is the +adopter-facing page. ## Behaviour & contract