Skip to content

Commit 474793d

Browse files
docs(upgrade): warn that v2 → v3 needs the compat plugins first (#135)
Claude Code namespaces commands by the plugin manifest name, so pre-v3 versions shipped /elixir-phoenix:* while /phx:init wrote /phx:* into CLAUDE.md. v3.0.0 fixed that by renaming the manifest to phx, and in the same commit declared ecto and lv as dependencies. Adding a dependency turns out to be a breaking change for installed users. claude plugin update does not install dependencies a new version newly declares, and a missing dependency is a hard load failure, so a plain /plugin update leaves the plugin at "failed to load" with all 36 /phx:* commands gone. Verified on Claude Code 2.1.234, so this is not the 2.1.76-2.1.109 band found during #130 - it hits every v2 user on every Claude Code version, and nothing self-heals because auto-update is off by default for non-Anthropic marketplaces. The README's staged upgrade block was already correct but called the failure "a missing-dependency state" and sat below the fold. It now leads with a warning, states the blast radius in commands lost, declares the 2.1.110 floor, adds a recovery path for anyone who already updated in the wrong order, and explains the prefix history. Also: the release skill gains an Iron Law and template for hoisting upgrade warnings to the top of the release body, and make validate now covers all five manifests instead of two - the same gap that let displayName ship in four manifests while CI checked one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 4274697 commit 474793d

5 files changed

Lines changed: 113 additions & 17 deletions

File tree

.claude/skills/release/SKILL.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ consistent. **Contributor tooling — not shipped in the plugin.**
2424
6. **ALWAYS leave a fresh empty `## [Unreleased]`** — one `[Unreleased]` becomes one version heading; re-add an empty one on top.
2525
7. **NEVER force-push**`git push --force` is hook-blocked here. If history needs rewriting, the user runs it via `!`.
2626
8. **EVERY release body links the docs site** — append the `https://phxagents.dev` footer. Releases are this project's one measured promotion lever (v3.0.1: 51 → 120 cloners in a day).
27+
9. **UPGRADE-BREAKING RELEASES LEAD WITH THE WARNING** — if users must do anything beyond `/plugin update`, the release body opens with a `> [!WARNING]` block carrying the exact commands (see #135).
2728

2829
## Step 0: Preconditions
2930

@@ -117,8 +118,8 @@ gh release create vX.Y.Z --title "vX.Y.Z — <summary>" --notes-file <changelog-
117118
```
118119

119120
Use the new CHANGELOG section as release notes (extract it to a temp file or `--notes`),
120-
then **append the docs footer** before publishing — a release body is read at the
121-
moment someone decides whether to install:
121+
then **prepend any upgrade warning** (Iron Law 9) and **append the docs footer** before
122+
publishing — a release body is read at the moment someone decides whether to install:
122123

123124
```
124125
---

.claude/skills/release/references/templates.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,32 @@ printf '\n---\n\nDocs, install guides, and the runtime compatibility matrix: <ht
5757
gh release create vX.Y.Z --title "vX.Y.Z — <summary>" --notes-file /tmp/relnotes.md
5858
```
5959

60+
## Upgrade warning block (Iron Law 9)
61+
62+
When the release requires anything beyond `/plugin update` — a new dependency,
63+
a renamed manifest, a manual migration — **prepend** this block so it is the
64+
first thing on the release page, above the changelog body:
65+
66+
```bash
67+
cat > /tmp/relnotes.md <<'EOF'
68+
> [!WARNING]
69+
> **Upgrading from vN.x requires these commands in this order.** <one line on
70+
> what breaks otherwise, in user-visible terms.>
71+
>
72+
> ```bash
73+
> <exact commands>
74+
> ```
75+
76+
EOF
77+
awk '/^## \[X\.Y\.Z\]/{f=1} f&&/^## \[/&&!/X\.Y\.Z/{exit} f' CHANGELOG.md >> /tmp/relnotes.md
78+
```
79+
80+
State the blast radius in what the user loses, not in mechanism. "The plugin
81+
fails to load — all 36 `/phx:*` commands disappear" lands; "enters a
82+
missing-dependency state" does not. v3.0.0 used the second phrasing, buried in
83+
a `### Changed` bullet, and users still upgraded into a broken install
84+
(issue #135).
85+
6086
Title format matches history: `vX.Y.Z — <short summary>` (em dash).
6187

6288
**The docs footer is not optional.** A release body is read at the exact moment

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5050
subagents were never affected — their own frontmatter wins — so
5151
`parallel-reviewer` and `planning-orchestrator` needed no change.
5252

53+
- **The `release` contributor skill hoists upgrade warnings to the top of the
54+
release body** — a new Iron Law and template: when a release needs anything
55+
beyond `/plugin update`, the body opens with a `> [!WARNING]` block carrying
56+
the exact commands, and states the blast radius in what the user loses rather
57+
than in mechanism. v3.0.0 documented its staged upgrade correctly but placed
58+
it at roughly line 145 of a long changelog dump, phrased as "a temporary
59+
missing-dependency state" — and users upgraded into a broken install anyway
60+
(#135). A correct instruction nobody reaches is indistinguishable from a
61+
missing one.
62+
5363
- **CLAUDE.md model-tier rules describe the actual split** — the guidance said
5464
"opus for primary workflow orchestrators, sonnet for secondary orchestrators
5565
(investigation, tracing)", which stopped matching the plugin once
@@ -60,6 +70,35 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6070

6171
### Fixed
6272

73+
- **Upgrading from v2.x no longer lands users in a dead install** (reported by
74+
@barquesurlocean, #135) — v3.0.0 renamed the plugin manifest to `phx` (which
75+
is what makes `/phx:*` correct; pre-v3 versions namespaced their commands as
76+
`/elixir-phoenix:*` while `/phx:init` wrote `/phx:*` into `CLAUDE.md`) and in
77+
the same commit introduced the `ecto` and `lv` compatibility plugins as
78+
manifest `dependencies`. Adding a dependency turns out to be a breaking
79+
change for already-installed users: `claude plugin update` does **not**
80+
install dependencies a new version newly declares, and a missing dependency
81+
is a hard load failure, so the obvious `/plugin update` leaves the plugin at
82+
`✘ failed to load` with all 36 `/phx:*` commands gone — taken down by the 3
83+
compatibility commands. Verified on Claude Code 2.1.234, so this is not the
84+
2.1.76–2.1.109 version band recorded during #130; it affects every v2 user on
85+
every Claude Code version. Nothing self-heals either, because auto-update is
86+
off by default for non-Anthropic marketplaces. The README's staged upgrade
87+
block was already correct but described the failure as "a missing-dependency
88+
state"; it now leads with a warning, states the blast radius in commands
89+
lost, declares the 2.1.110 version floor, adds a recovery path for anyone who
90+
already updated in the wrong order, and explains the `/phx:` vs
91+
`/elixir-phoenix:` prefix history. Note during recovery that
92+
`claude plugin install elixir-phoenix@oliver-kriska` resolves only **one**
93+
missing dependency per invocation.
94+
95+
- **`make validate` covers every plugin manifest** — the target validated
96+
`plugins/elixir-phoenix` and the marketplace only, so `plugins/ecto`,
97+
`plugins/lv`, and `plugins/catchup` could ship a schema violation that CI
98+
never saw. This is exactly how `displayName` reached users: it was present in
99+
all four Claude-facing manifests while the gate looked at one of them. All
100+
five manifests are validated now.
101+
63102
- **`displayName` removed from all Claude Code manifests** (reported by
64103
@ndrean, #130) — the field was introduced alongside the v3 plugin split and
65104
reported as an install blocker one day later. It is undocumented: it appears

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ test-quick: ## Run pytest (no verbose, fast)
5959

6060
# --- Validate ---
6161

62-
validate: ## Run claude plugin validate on plugin + marketplace manifests
62+
validate: ## Run claude plugin validate on every plugin + marketplace manifest
6363
@claude plugin validate plugins/elixir-phoenix
64+
@claude plugin validate plugins/ecto
65+
@claude plugin validate plugins/lv
66+
@claude plugin validate plugins/catchup
6467
@claude plugin validate .
6568

6669
amp-skills: ## Generate Amp skills from the canonical Claude plugin

README.md

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,23 @@ that prevent the mistakes Elixir developers actually make in production.
140140
/plugin install elixir-phoenix
141141
```
142142

143-
The install name stays `elixir-phoenix`, while its public workflow commands
144-
remain `/phx:*`. Claude Code now derives plugin command names from the plugin
145-
namespace and each skill's final command name. On a fresh install, it also pulls
146-
in two small compatibility namespaces for `/ecto:*` and `/lv:*` automatically;
147-
you do not need to install them separately. Existing v2 installations should
148-
follow the staged upgrade below. After an update, run `/reload-plugins` before
149-
trying the commands in an already-open session.
143+
Requires **Claude Code 2.1.110 or newer**. The install name stays
144+
`elixir-phoenix`, while its public workflow commands remain `/phx:*` — Claude
145+
Code namespaces commands by the plugin's manifest name, not its install name.
146+
A fresh install also pulls in two small compatibility namespaces for `/ecto:*`
147+
and `/lv:*` automatically; you do not need to install them separately. After an
148+
update, run `/reload-plugins` before trying the commands in an already-open
149+
session.
150150

151151
#### Updating from v2.x
152152

153-
Update the marketplace, install the two compatibility namespaces introduced in
154-
v3, then update the main plugin:
153+
> [!WARNING]
154+
> **Do not run `claude plugin update` on its own.** v3 introduced two
155+
> compatibility plugins (`ecto`, `lv`) that v2 never declared, and
156+
> `claude plugin update` does not install dependencies a new version newly
157+
> declares. The updated plugin then fails to load entirely — you lose all 36
158+
> `/phx:*` commands, not just `/ecto:*` and `/lv:*`. Install the two
159+
> compatibility plugins **first**, using the exact order below.
155160
156161
```bash
157162
claude plugin marketplace update oliver-kriska
@@ -160,11 +165,33 @@ claude plugin install lv@oliver-kriska
160165
claude plugin update elixir-phoenix@oliver-kriska
161166
```
162167

163-
Installing the compatibility plugins first prevents the updated main plugin
164-
from entering a missing-dependency state. Restart Claude Code afterward. In an
165-
already-open session, `/reload-plugins` reloads the updated skills, agents, and
166-
hooks. Confirm that `/phx:help`, `/ecto:n1-check`, and `/lv:assigns` appear
167-
before continuing work.
168+
Restart Claude Code afterward. In an already-open session, `/reload-plugins`
169+
reloads the updated skills, agents, and hooks. Confirm that `/phx:help`,
170+
`/ecto:n1-check`, and `/lv:assigns` appear before continuing work.
171+
172+
##### Already updated in the wrong order?
173+
174+
If `claude plugin list` reports `✘ failed to load` with
175+
`Dependency "ecto@oliver-kriska" is not installed`, install the two
176+
compatibility plugins to recover — nothing is lost, and no reinstall is needed:
177+
178+
```bash
179+
claude plugin install ecto@oliver-kriska
180+
claude plugin install lv@oliver-kriska
181+
```
182+
183+
Note that `claude plugin install elixir-phoenix@oliver-kriska` also repairs the
184+
state, but resolves only **one** missing dependency per run, so it needs two
185+
invocations here.
186+
187+
#### Commands are `/phx:*`, not `/elixir-phoenix:*`
188+
189+
Plugin versions before v3.0.0 shipped a manifest named `elixir-phoenix`, so
190+
their commands resolved as `/elixir-phoenix:work` even though `/phx:init` wrote
191+
`/phx:*` into `CLAUDE.md`. v3.0.0 renamed the manifest to `phx`, which is what
192+
makes `/phx:*` correct. If Claude Code reports `/phx:` commands as unknown,
193+
you are on a pre-v3 version — follow the upgrade steps above, then re-run
194+
`/phx:init --update`.
168195

169196
#### Claude Code subagent compatibility
170197

0 commit comments

Comments
 (0)