config-audit v5.12.0 — release. Catalog ref now pins the v5.12.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config-audit v5.11.0 — release. Catalog ref now pins the v5.11.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ms-ai-architect v1.16.1 — release. Catalog ref now pins the v1.16.1 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
okr v1.3.1 — release. Catalog ref now pins the v1.3.1 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
graceful-handoff v3.0.0 — release. Catalog ref now pins the v3.0.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config-audit v5.10.0 — release. Catalog ref now pins the v5.10.0 tag so
`claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config-audit v5.9.0 — release. Catalog ref now pins the v5.9.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
linkedin-studio v0.5.2 — release. Catalog ref now pins the v0.5.2 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
linkedin-studio v0.5.1 — release. Catalog ref now pins the v0.5.1 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config-audit v5.8.0 — release. Catalog ref now pins the v5.8.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ms-ai-architect v1.16.0 — release. Catalog ref now pins the v1.16.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The catalog README's per-plugin `vX.Y.Z` labels were an UNGUARDED surface:
check-versions validated each plugin's OWN README badge, never the catalog
README's labels, so they drifted (config-audit shown v5.5.0 while pinned to
v5.7.0; voyage v5.1.1 while pinned to v5.6.0) — the doc misstated what
`claude plugin install` actually resolves.
Close the class, same pattern as the ref surface:
- check-versions.mjs: new ERROR rule "catalog README label == catalog ref"
via pure extractCatalogLabel() (matches the /open/<name>) heading, takes the
first `vX.Y.Z`, ignores a trailing lang/flag badge). No legitimate transient
state lets label != ref, so ERROR (not WARN). +5 tests.
- release-plugin.mjs: on --write, bump the README label atomically with the ref
via pure reconcileReadmeLabel() and git add README.md on --commit, so future
releases keep label and ref in lock-step. +4 tests.
- README.md: reconcile the 2 stale labels (config-audit -> v5.7.0,
voyage -> v5.6.0). Gate now 9 OK / 1 WARN / 0 ERROR.
- CLAUDE.md: doctrine updated to document the new gate rule + atomic label bump.
ms-ai-architect stays WARN by decision (1.16.0 is unreleased WIP, never tagged).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cm2RxKbomdLqjiWGcwCCPi
config-audit v5.7.0 — release. Catalog ref now pins the v5.7.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The polyrepo split made a plugin release a TWO-repo act: tag the plugin repo AND
bump the catalog `ref`. The second step is manual and easily forgotten — that
drift just stranded linkedin-studio on v0.4.0 while its plugin.json moved to
0.5.0. This adds the canonical release path that makes the catalog side
impossible to do wrong.
scripts/release-plugin.mjs:
- Pure planner planRelease() — given the catalog + observed plugin state +
target version, computes verdict (READY/NOOP/BLOCKED), the bumped marketplace
object, and the commit subject. REFUSES (BLOCKED) unless plugin.json == README
badge == target AND the vX.Y.Z tag exists, so a READY plan is check-versions-
green by construction. Reuses normalizeVersion/classifyPlugin from
check-versions.mjs (no duplicated rules).
- I/O shell: dry-run by default; --write bumps the ref + re-runs the gate;
--commit/--push apply; --create-tag mints+pushes a missing plugin tag first.
- 10/10 unit tests (scripts/release-plugin.test.mjs); check-versions 9/9 still green.
- Dogfooded: linkedin-studio → NOOP (already pinned v0.5.0); ms-ai-architect →
BLOCKED (v1.16.0 in plugin.json was never tagged — refuses to publish it).
Also: fix the stale LinkedIn Studio README label v0.4.0 -> v0.5.0 (loose end of
the v0.5.0 release), and document the canonical release path in CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
voyage v5.6.0 — /trekexecute loop hardening (machine-verifiable completion
gate, cap-25 recovery budget, iterations_remaining field). Catalog ref now
pins the v5.6.0 tag so `claude plugin update` resolves the release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
llm-security cut v7.8.0 (TRG/SIG/AST deep-scan scanners) and tagged `v7.8.0`
(commit 6d3c4b5) in its repo. Sync the marketplace coordinator:
- .claude-plugin/marketplace.json: source.ref v7.7.2 → v7.8.0 (installs track the
real release; version-consistency gate → OK for llm-security).
- README.md: version label v7.7.2 → v7.8.0; scanner count 23 → 26 (+3: TRG/SIG/AST)
in both the "Deterministic scanning" bullet and the stat line; tests 1822 → 1863.
- docs/state-version-rollout.md: mark llm-security STATE.md row DONE (it is tracked,
not gitignored), add the missing playground-design-system row, and refresh the
Workstream B run note (8 OK / 2 WARN; llm-security resolved this day).
Gate: `node scripts/check-versions.mjs` → 8 OK, 2 WARN (linkedin-studio,
ms-ai-architect — pre-existing, operator decision), 0 ERROR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3s6WnubSSrFjAQTLQdVbG
Plugin v5.5.0 (steering-model I: A+E) is tagged and pushed; this points
the marketplace source.ref at it so installs track the real release.
Scanner count unchanged (13), so the README prose stays accurate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
Marketplace-wide coordination brief in catalog/docs/. Two cross-cutting workstreams:
(A) STATE.md tracked in every repo per the updated global continuity rule —
verified status: 2 DONE (config-audit, ms-ai-architect), 3 avgitignore+track
(voyage, linkedin-studio, claude-design), 6 avgitignore-only; per-repo steps +
testable verification. (B) version-consistency via check-versions.mjs — 8 OK,
2 WARN (linkedin-studio, ms-ai-architect: plugin.json ahead of an untagged ref),
each needing an operator release-intent decision. Plan only — execution is per
repo with its own GO.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
scripts/check-versions.mjs cross-checks every plugin in marketplace.json against
its sibling repo:
- catalog ref must resolve to a real git tag → ERROR (dangling = install breaks)
- plugin.json version == README version-badge → ERROR (internal corruption)
- catalog ref == plugin.json version → WARN (catalog lags / unreleased bump)
- sibling repo missing → SKIP
Exit 1 on any ERROR; --strict also fails on WARN. Pure classifier covered by
check-versions.test.mjs (9 tests, node --test). Documented in CLAUDE.md
catalog-maintenance. Run before committing any ref change.
Current run: 8 OK, 2 WARN (linkedin-studio, ms-ai-architect — catalog ref lags an
untagged plugin.json bump), 0 ERROR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
Forgejo rate-limits rapid port-22 handshakes. The bare per-target
`git push --all` + `git push --tags` opened ~22 SSH connections in rapid
succession; the server refused around the 6th (voyage's tag push) with
"port 22: Connection refused" (TCP-level, not auth), deterministically, on
every run. Export GIT_SSH_COMMAND with ControlMaster/ControlPath/ControlPersist
so all git-over-SSH reuses ONE persistent master connection (one TCP handshake
for the whole rollout).
Verified: 8 rapid multiplexed connections all succeed where the 6th bare
connection is refused; the full 11-target rollout then completed clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The operator window's step [a] called 40-validate-standalone.sh directly (strict
exit-code), so it STOPped on the first target carrying pre-existing in-repo test
red — voyage (2 doc-consistency drifts re phase_models/phase_signals, content moved
to docs/operations.md) and ai-psychosis (1). But the migration's ratified contract,
the one the Step-11 dry-run validated as PASS 11/11, is 'introduce no regression':
pre-existing in-repo red is the plugin's own concern, not a migration regression.
The window enforced a STRICTER gate than the contract the dry-run signed off.
Fix: new 41-validate-or-regression.sh — the single per-target gate the window calls
in [a]. It runs 40 strict, then on failure passes iff the standalone failing-test
NAME set is a SUBSET of the live in-repo set (the exact decision 99-dryrun.sh makes),
reusing sc2-regression.sh. A genuine extraction-introduced regression still STOPs the
window; a structure-validator fail and the config-audit gate stay strict.
Single-source the failing-name capture: extract capture_fails into capture-fails.sh
(mirrors the sc2-regression.sh extraction) so the live gate and the dry-run agree on
what 'failing' means; 99-dryrun.sh now delegates to it (behaviour identical).
Verified end-to-end on the real extracts: 40 strict fails voyage+ai-psychosis while
41 passes them 'N pre-existing, regression-relative'; clean targets (llm-security,
graceful-handoff) still pass via the strict path. New hermetic tests: capture-fails
3/3, 41 6/6 (strict-pass, regression-relative-pass, genuine-regression-fail,
structure-not-eligible, gate pass/fail). RUNBOOK per-repo step updated to 41.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pilot died at line 93 with 'key<byte>: unbound variable': $key glued to
the U+2026 ellipsis let bash 5.3.9 read the multibyte byte as part of the name.
Braced to ${key}…; proven on /usr/local/bin/bash (repro + fixed form + bash -n).
Nothing was mutated on Forgejo — it failed before the [b] repo-create POST.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review of the rollout script found 2 deterministic blockers on target #2,
playground-design-system — a shared component, not a marketplace plugin:
- step (d): DS has no .claude-plugin/plugin.json (it ships tokens.css/base.css/
schemas). The plugin.json assertion would die. Now DS-aware: assert tokens.css
for DS_KEY, plugin.json otherwise.
- step (e): 60-rewrite --only playground-design-system errors (--only name not in
marketplace.json) since DS has no catalog entry. New entry_present() gate skips
the flip when the key has no marketplace entry, mirroring 60-rewrite --all
(which only touches the 10 real plugins).
Net: pilot + 9 plugins flip (count_local to 0); DS is stood up as a repo but
never flipped. Verified: bash -n OK, entry_present + 60-rewrite behaviour tested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-review scope a44e37b..85a8ee3. BLOCK (1+6+3) → WARN (2 new MAJOR surfaced in
30-fix-references.mjs) → ALLOW (both remediated, re-verified by the independent
code-correctness reviewer). 0 BLOCKER / 0 MAJOR / 0 MINOR / 0 SUGGESTION.
Operator window (RUNBOOK.md) no longer gated by a review BLOCK.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The /trekreview re-review's high-effort deep read of 30-fix-references.mjs
surfaced 2 MAJOR (both pre-existing in the original delivery, not remediation
regressions):
- PLAN_EXECUTE_DRIFT: the rewriter set repository.url but left a monorepo-relative
repository.directory (e.g. llm-security's plugins/llm-security) intact, so the
standalone package.json/plugin.json shipped a directory pointing nowhere. Now
dropped in BOTH the plugin.json (3a) and package.json (3b) object branches;
idempotency preserved (gated on 'directory' in repository).
- MISSING_TEST: the package.json rewrite branch only ran against voyage/llm-security
but the only test used graceful-handoff (no package.json) — unguarded. Added a test
driving both branches against a synthetic llm-security extract (pre-init .git,
fixtures carrying repository.directory), asserting reconciliation + directory-drop
on both files + idempotency.
Independent code-correctness reviewer re-verified both RESOLVED, no new issue.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MAJOR
- 9e97cd5 40-validate-standalone.sh: route a target's sc2_gate to its dedicated
gate (config-audit → 50-config-audit-sc2.sh), mirroring 99-dryrun.sh, so --all
no longer falsely FAILs config-audit on the machine-locked v5.0.0 tests.
- 1708e90 99-dryrun.sh: assert EXACTLY one tag survives (F5); a partial tag-strip
no longer silently reports the wrong tag via head -1.
- 4e494c8 99-dryrun.sh: capture the SC2 standalone failing set from the dry-run's
own prepped extract ($dest), not the 40-validate side-effect clean room.
- aeb6292 00-preflight.sh: assert every map path is whitespace/glob-free, making
the word-split path handling in 99-dryrun.sh sound.
- 5d112cb extract the SC6 DROP + SC2 regression detectors into sc6-check.sh /
sc2-regression.sh and add sc-checks.test.mjs — a negative test proving each
detector FIRES (force-fresh re-extraction would undo a planted file-drop).
- 9e588ca 10-extract.sh re-asserts git filter-repo before use (self-heal runs
preflight only on a missing mirror); RUNBOOK lists git-filter-repo + python3>=3.6.
MINOR
- bc0f8a7 plugin-map.json: reset ms-ai-architect blob_strip_safe to null
(00-preflight.sh populates it per run).
- 8d649e9 99-dryrun.sh: gate SC6 behind extract success; a failed extract is
labelled (extract failed), not a content DROP.
- 4044c49 99-dryrun.sh: guard mktemp — an empty capture is an error, not a
false zero-regression PASS.
Also: 00-preflight.test.mjs asserted all 3 'renamed' plugins carry >=2 paths, but
llm-security became single-path in 836b8e9 (copilot was a coexisting plugin, not a
rename) — a stale pre-existing failure. Aligned the test to the ratified map and
added a positive single-path lock against re-introducing the 87-file-drop defect.
Verified: full dry-run 11/11, 0 pushes; sc-checks/99-dryrun/40-validate/00-preflight/
60-rewrite suites green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60-rewrite-marketplace.mjs:87 emitted a flat { source: 'url', url, ref }
shape. The official Claude Code marketplace schema (verified at
code.claude.com/docs/en/plugin-marketplaces) and brief §6 both require the
nested form { source: { source: 'url', url, ref } } — a flat shape would not
resolve at install, breaking SC1/SC3/SC8 for every externalised entry.
- l.87: emit nested source-object
- validate(): branch on object (external) vs string (local ./plugins/) source
- 60-rewrite-marketplace.test.mjs: assert nested voyage.source.source / .url / .ref
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RUNBOOK.md: the single doc the operator follows in the authorized window. Pilot gate on graceful-handoff with the B3 ordering fix (flip the catalog entry to external BEFORE the install-smoke, else the Forgejo/HTTPS/ref chain is untested). Forgejo repo creation via API (auto_init:false, public), per-repo create->push->flip->install-smoke loop, batch order (design-system -> high-churn -> low-churn), thin-catalog last, ref-update flow, and a reversible-until-thinning rollback. References the prior scripts; push-window-aware.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>