ktg-plugin-marketplace/CLAUDE.md
Kjell Tore Guttormsen e17229fb78 docs(catalog): count prompt-hook entries on the badge-less hooks axis
The counting rule said "hook COMMAND entries". It was calibrated against
plugins that predate prompt-hooks, so it was never a ruling on them. A prompt
entry fires on the same event and does the same job, so it counts.

Re-measured every pinned ref before rewriting the rule, since restating an old
measurement in new words is itself a claim: every plugin that badges hooks and
ships a hooks.json has entries equal to command-entries, so the two phrasings
agree across the whole badged set. okr is the only plugin with a non-command
entry, and it does not badge the axis.

Also records which plugins badge the agents axis and which two do not, so a
future session does not treat an unmeasured number as gated or a gated one as
unmeasured.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141o7P9gFCDxURKyc2cW7mc
2026-08-13 20:40:00 +02:00

9.6 KiB
Raw Blame History

ktg-plugin-marketplace (catalog)

Catalog repository for the ktg-plugin-marketplace. After the polyrepo migration this repo hosts only the marketplace manifest and the catalog-level docs; every plugin and the shared design-system live in their own Forgejo repositories under https://git.fromaitochitta.com/open/.

What lives here

  • .claude-plugin/marketplace.json — the marketplace manifest (plugin entries point at external repos)
  • README.md — the landing/catalog page
  • CONVENTIONS.md — marketplace-wide conventions inherited by every plugin repo
  • GOVERNANCE.md — governance + fork-and-own model
  • .mailmap, .gitleaks.toml, .gitleaksignore — shared git-hygiene baselines

Catalog maintenance

  • Marketplace conventions: see CONVENTIONS.md.
  • Adding/updating a plugin entry: edit .claude-plugin/marketplace.json (external source: "url" with a pinned ref) and re-state the plugin in README.md with its verified version.
  • Plugin source, issues, and releases live in each plugin's own repository — not here.
  • Releasing a plugin (canonical path — scripts/release-plugin.mjs): since the polyrepo split, a release is a TWO-repo act — tag the plugin repo AND bump the catalog ref. Forgetting the second step strands users on the old version (the exact drift this helper exists to prevent). Run node scripts/release-plugin.mjs <plugin> [--version X.Y.Z] — dry-run by default; it REFUSES unless plugin.json == README badge == the target version AND the vX.Y.Z tag exists, then prints the planned bump. Apply with --write [--commit] [--push]; --create-tag --write mints+pushes a missing plugin tag first (--create-tag is a WRITE and obeys --write — on a dry-run it only reports what it would mint). On --write it bumps the catalog ref AND the catalog README's per-plugin `vX.Y.Z` label together (and git adds both on --commit). Because it only moves both to a verified, tagged, consistent version, check-versions.mjs is green by construction. Never hand-edit a ref or a README label for a release — use this. Pure planner + label reconciler + pre-flight/write step covered by scripts/release-plugin.test.mjs.
  • Pre-flight gate (--write runs check-versions BEFORE it writes): the helper calls runGate() first and aborts with exit 1 — nothing written — if ANY plugin is ERROR, not just the one being released (check-versions' exit code is catalog-wide). Previously the gate ran after both writes, so a red catalog left a half-applied release in the working tree for a parallel session to carry to the public remote. The pre-flight reads the ERROR set only, never failed/--strict: pre-bump, the plugin being released is supposed to be WARN (catalog ref behind plugin.json), so gating on WARN would brick every release. The post-write gate at the end stays — pre-flight validates the old state, that one validates the new state. --create-tag is deliberately NOT behind this gate (decided 2026-08-10): it mints and pushes the plugin tag before the pre-flight runs, but every precondition it checks is local to that one plugin (plugin.json == target, badge agrees, tag absent), so the tag is correct by construction. A red other plugin can only make the tag EARLY, never WRONG, and the tag-absent check makes the retry idempotent — gating it would let plugin Y block the tagging of plugin X, the same over-coupling that reading ERROR-only avoids. What WAS closed is the worse half: --create-tag used to push on the documented dry-run path, with no --write at all. It now requires --write (shouldCreateTag, tested).
  • Version-consistency gate: run node scripts/check-versions.mjs before committing any ref change. For each plugin it checks (against the sibling repo) that the catalog ref resolves to a real git tag (ERROR if dangling — breaks install), that plugin.json version == README version-badge (ERROR), that the catalog README's per-plugin `vX.Y.Z` label == the catalog ref (ERROR — the human-facing doc must not misstate the installed version), and that the catalog ref matches plugin.json version (WARN — catalog lags or an unreleased bump). Exit 1 on any ERROR; --strict also fails on WARN. Pure-function core covered by scripts/check-versions.test.mjs (node --test scripts/check-versions.test.mjs).
  • Stat-badge mirroring (part of the same gate): each plugin block in the catalog README ends in a stat line (7 agents · 16 scanners · 21 commands · 1398 tests · [Full documentation →]). The gate compares every number on that line against the plugin's own shields badge for the same axis, and ERRORs when they disagree — the catalog must not overstate a plugin. The rule is per-AXIS, not per-plugin: an axis the plugin does not badge is skipped silently, so there is no exception list to maintain. Re-measured 2026-08-04 at the pinned refs, all 12 plugins: 42 catalog axis-claims — 27 badge-covered · 15 badge-less across 7 of the 12 plugins and therefore ungated. That supersedes the 2026-08-02 reading (24 · 15 across 7 of 11); only two entries moved in between and both reconcile exactly — repo-mailbox now badges the two axes it did not (+2 gated, -2 ungated) and repo-standard entered the catalog (+1 gated, +2 ungated). The 15 ungated axes were hand-audited at their pinned tags — 13 on 2026-08-02 (14 of that pass's 15 were exact; the defects were repo-mailbox 6 CLI scripts (true 8) / 251 selftest checks, and voyage 500+ tests against a measured 832), and repo-standard's two on 2026-08-04. Every ungated value currently stands measured at the ref it is pinned to — verified on both sides 2026-08-04, because a ref that has not moved only proves the badge source is unchanged and catalog prose is itself ungated: no ungated plugin's ref has moved since its audit, and diffing extractCatalogStats against the 08-02 README shows the only ungated numbers the catalog has restated since are repo-standard's two (absent then). ⚠️ The 251 selftest checks correction is not fully settled, though the weight is on 370: this file and the catalog's own 08-02 stat line both record the axis as corrected to 370, check-versions.mjs said 374 (most likely a transcription slip), and repo-mailbox's README at v0.19.0 implies 390 (183 + 134 + 73) — its own prose being ungated too. Moot for the gate (they badge the axis now, 398 at v0.20.2, green) and not re-measured. It stands as the proof of the cost: an ungated number rots, and so does the record of having fixed it. N+ in the catalog is read as a lower bound, not an equality, so 500+ was never gate-visible — ungated axes rot in silence and need a re-run of this pass whenever a ref moves. Never hand-edit a stat line to silence the gate — the plugin's badge is the source for every stat number; fix the catalog to match it.
  • Counting rules for a badge-less axis (calibrated against the badged plugins, 2026-08-02). When the catalog must count an axis itself, count it the way the badges do, or the numbers stop being comparable across plugin blocks: hooks = hook ENTRIES in hooks/hooks.json (not events, not matchers — the three diverge), and tests = tests from node --test, not pass (config-audit's badge 1398 is its tests count; pass was 1375). Measure in an extraction of the tag (git archive <ref> | tar -x -C <tmp>), never the sibling working tree.
    • type: prompt entries COUNT (decided 2026-08-13). The rule read "hook COMMAND entries" until now. That wording was calibrated in 2026-08 against plugins that predate prompt-hooks, so it was never a ruling on them — it had no case to rule on. A prompt entry fires on the same event and does the same job from the reader's side, so excluding it would understate the plugin. Re-measured 2026-08-13 at all 12 pinned refs before the rewrite, because restating an old measurement in new words is itself a claim: every plugin that both badges hooks and ships a hooks.json has entries == command-entries (llm-security 9/9, config-audit 4/4, linkedin-studio 9/9, ai-psychosis 4/4, repo-mailbox 1/1), so the two phrasings agree on the whole badged set and the rewrite contradicts nothing. okr @ v1.10.0 is the only plugin with a non-command entry — 3 entries, 2 command + 1 prompt (PreCompact) — and it does not badge the axis, which is precisely why the badged set never tested the rule. The catalog's 3 hooks stands, measured.
    • The agents axis IS badge-covered — 4 of the 6 plugins that claim it (measured 2026-08-13). llm-security agents-6, config-audit agents-7, ms-ai-architect agents-12, linkedin-studio agents-20; all four match the catalog exactly and are gated. Ungated: voyage (24 agents) and okr (7 agents) — badge-less, so those two numbers are unverified until measured at their refs. Do not record this axis as uncalibrated: it has a referent.
  • The stat mirror reads the plugin README AT THE PINNED ref, never the sibling working tree. The catalog documents what installs, and that is the tag. A plugin that commits past its tag without bumping its version — measured 2026-08-02 on both llm-security (scanners 23→22, tests 2013→2034) and config-audit (tests 1398→1441) — would otherwise make the gate demand that the catalog restate unreleased numbers, which is exactly backwards. When the gate flags a stat, check git show <ref>:README.md in the plugin repo before believing the working tree.