#362: make the plugin visible, and fix the drift that made it unusable #363

Merged
ltms merged 1 commits from 362-plugin-visibility-and-drift into main 2026-09-05 07:50:19 +02:00
Owner

Closes the first two scope items of #362. Item 3 (seeding .claude/skills/ into provisioned worktrees) is a separate branch.

Why

CB-527 shipped a Claude Code plugin and a marketplace in this repo. In September 2026 a session planned the same feature from scratch, because nothing in CLAUDE.md or docs/ named it.

The wiki/11-Features.md entry did exist and was correct. It did not help: wiki/ is a submodule whose pointer is never advanced, so no session reads it by default. That is the real defect — a documentation-channel problem, not a missing-documentation problem.

Visibility

  • CLAUDE.md addendum now names plugin/ and both structural limits. This is the change that stops the rebuild recurring — it is the only file every session loads.
  • wiki/11-Features.md records the rename and, explicitly, that the entry alone was not enough.

Drift fixed — each measured against the code

Change Why
mount name fleetd → fleet matches PeerLauncher.java:34 MCP_MOUNT_NAME. The old name gave a lead with both a project .mcp.json and the plugin two mounts of one daemon and a duplicated fleet_* tool set
url → ${FLEETD_MCP_URL} one plugin can now serve hosts running the daemon on different ports
plugin claude-bridge → fleet, marketplace → fleetd, v0.2.0 the project renamed in CB-634
README install path the repo is fleet/fleetd since CB-623, not ltms/claude-bridge
setup §5 primary.terminal: → fleet.leaders.*.tab CB-579 replaced the pin. The old advice sent operators down a dead path

Breaking for a 0.1.0 install. A project that pre-allowed mcp__fleetd__fleet_whoami in .claude/settings.json must be updated to mcp__fleet__*. Only this fleet has it installed, so the cost is small now and grows.

I used the plain ${VAR} form, not ${VAR:-default}. kb-alms proves the plain form works in a real installed plugin on this machine; the default-value form is untested, and I was not willing to put an unverified expansion in the one file every session's mount depends on. The setup skill now checks the variable in preflight.

Scope: the plugin is lead-side only, and cannot be otherwise

Two structural reasons, both verified in the code, not assumed:

  • ClaudeCodeLauncher.java:371 calls agentDefinitionFile(spec.cwd(), spec.role(), ".claude", "agents"); HerdrPeerLauncher.java:359-365 returns a path only when <cwd>/.claude/agents/<role>.md is a regular file; :391-393 adds --agent only when that is non-null. Agent files must live in the member's worktree.
  • ClaudeCodeLauncher.java:285 exports CLAUDE_CONFIG_DIR from the profile's configDir. All four Claude profiles on the live Mac fleet set it, and the four ccs instances hold four separate copies of the plugin store — same md5 (51c6e1c853e32e656b817e123fbbfcc5), different inodes. A member never reads the operator's plugin store.

So member-facing assets travel in the worktree. That is #362 item 3.

Verification

  • claude plugin validate ./plugin → passes clean.
  • mvn -o test -Dtest=McpContractDocTest → Tests run: 3, Failures: 0, Errors: 0.
  • The canonical CLAUDE.md block is still byte-identical with the wiki template (the repo's own python check prints in sync: True). My edit was in the project addendum, not the block.

A warning worth keeping: claude plugin validate does not read .mcp.json. I replaced the file with { this is not json at all and validation still passed with exit 0. It validates the manifest only. Do not use it as evidence a mount is correct — I nearly did.

Not done here

  • plugin/ still ships only the setup skill. The bridge-charter skill, which would end the hand-copied CLAUDE.md block, is a later stage.
  • #359 (duplicate lead tabs stall coordination) must land before anything encourages a second lead per host.
Closes the first two scope items of #362. Item 3 (seeding `.claude/skills/` into provisioned worktrees) is a separate branch. ## Why CB-527 shipped a Claude Code plugin and a marketplace in this repo. In September 2026 a session planned the same feature from scratch, because nothing in `CLAUDE.md` or `docs/` named it. The `wiki/11-Features.md` entry **did** exist and was correct. It did not help: `wiki/` is a submodule whose pointer is never advanced, so no session reads it by default. That is the real defect — a documentation-*channel* problem, not a missing-documentation problem. ## Visibility - `CLAUDE.md` addendum now names `plugin/` and both structural limits. This is the change that stops the rebuild recurring — it is the only file every session loads. - `wiki/11-Features.md` records the rename and, explicitly, that the entry alone was not enough. ## Drift fixed — each measured against the code | Change | Why | |---|---| | mount name `fleetd` → **`fleet`** | matches `PeerLauncher.java:34` `MCP_MOUNT_NAME`. The old name gave a lead with both a project `.mcp.json` and the plugin **two mounts of one daemon** and a duplicated `fleet_*` tool set | | url → **`${FLEETD_MCP_URL}`** | one plugin can now serve hosts running the daemon on different ports | | plugin `claude-bridge` → **`fleet`**, marketplace → **`fleetd`**, v0.2.0 | the project renamed in CB-634 | | README install path | the repo is `fleet/fleetd` since CB-623, not `ltms/claude-bridge` | | `setup` §5 `primary.terminal:` → **`fleet.leaders.*.tab`** | CB-579 replaced the pin. The old advice sent operators down a dead path | **Breaking for a 0.1.0 install.** A project that pre-allowed `mcp__fleetd__fleet_whoami` in `.claude/settings.json` must be updated to `mcp__fleet__*`. Only this fleet has it installed, so the cost is small now and grows. I used the plain `${VAR}` form, not `${VAR:-default}`. `kb-alms` proves the plain form works in a real installed plugin on this machine; the default-value form is **untested**, and I was not willing to put an unverified expansion in the one file every session's mount depends on. The `setup` skill now checks the variable in preflight. ## Scope: the plugin is lead-side only, and cannot be otherwise Two structural reasons, both verified in the code, not assumed: - `ClaudeCodeLauncher.java:371` calls `agentDefinitionFile(spec.cwd(), spec.role(), ".claude", "agents")`; `HerdrPeerLauncher.java:359-365` returns a path only when `<cwd>/.claude/agents/<role>.md` is a regular file; `:391-393` adds `--agent` only when that is non-null. **Agent files must live in the member's worktree.** - `ClaudeCodeLauncher.java:285` exports `CLAUDE_CONFIG_DIR` from the profile's `configDir`. All four Claude profiles on the live Mac fleet set it, and the four `ccs` instances hold four separate copies of the plugin store — same md5 (`51c6e1c853e32e656b817e123fbbfcc5`), different inodes. **A member never reads the operator's plugin store.** So member-facing assets travel in the worktree. That is #362 item 3. ## Verification - `claude plugin validate ./plugin` → passes clean. - `mvn -o test -Dtest=McpContractDocTest` → **Tests run: 3, Failures: 0, Errors: 0**. - The canonical `CLAUDE.md` block is still byte-identical with the wiki template (the repo's own python check prints `in sync: True`). My edit was in the project addendum, not the block. **A warning worth keeping:** `claude plugin validate` does **not** read `.mcp.json`. I replaced the file with `{ this is not json at all` and validation still passed with exit 0. It validates the manifest only. Do not use it as evidence a mount is correct — I nearly did. ## Not done here - `plugin/` still ships only the `setup` skill. The bridge-charter skill, which would end the hand-copied `CLAUDE.md` block, is a later stage. - #359 (duplicate lead tabs stall coordination) must land before anything encourages a second lead per host.
ltms added 1 commit 2026-09-05 07:43:40 +02:00
#362: make the plugin visible, and fix the drift that made it unusable
CI / contract (pull_request) Successful in 1m12s
CI / build (pull_request) Successful in 1m31s
457458437f
CB-527 shipped a Claude Code plugin and a marketplace in this repo. Nothing in
CLAUDE.md or docs/ ever named it, so a later session planned the same feature
from scratch. The wiki Features entry existed and was correct, but wiki/ is a
submodule whose pointer is never advanced, so no session reads it.

Visibility:
- CLAUDE.md addendum now names plugin/ and both structural limits, so every
  session sees it. This is the change that stops the rebuild happening again.
- wiki/11-Features.md records the rename and why the entry alone was not enough.

Drift (each measured against the code, not assumed):
- mount name fleetd -> fleet, matching PeerLauncher.MCP_MOUNT_NAME. The old name
  gave a lead with both a project .mcp.json and the plugin two mounts of one
  daemon and a duplicated fleet_* tool set.
- url is now ${FLEETD_MCP_URL} instead of a hardcoded address, so one plugin can
  serve hosts running the daemon on different ports. Plain ${VAR}, the form
  kb-alms proves works here; ${VAR:-default} is untested and not used.
- plugin claude-bridge -> fleet, marketplace claude-bridge -> fleetd, version
  0.2.0. Breaking for a 0.1.0 install: mcp__fleetd__* becomes mcp__fleet__*.
- README install path ltms/claude-bridge -> the fleet/fleetd remote.
- the setup skill's §5 told operators to pin primary.terminal:. CB-579 replaced
  that with fleet.leaders.*.tab. Replaced, with the duplicate-tab warning (#359).

Scope: the plugin is lead-side only, and cannot be otherwise. The launcher adds
--agent only when <worktree>/.claude/agents/<role>.md exists in the member's own
tree (ClaudeCodeLauncher.java:371,391), and a member's CLAUDE_CONFIG_DIR points
at its profile's config dir (ClaudeCodeLauncher.java:285), so a member never
reads the operator's plugin store. On this Mac all four Claude profiles set
configDir, and the four ccs instances hold four separate copies of the plugin
store -- same md5, different inodes. Seeding member skills through the worktree
is #362 scope item 3, implemented separately.

Note for anyone verifying a plugin: `claude plugin validate` does NOT read
.mcp.json. Replacing it with `{ this is not json at all` still passes, exit 0.

Refs #362, #359
Author
Owner

Correction to the "Breaking for a 0.1.0 install" paragraph. I wrote "Only this fleet has it installed". I had not measured that. I have now:

ltms: fleet/bridge plugins installed = NONE
gx10: fleet/bridge plugins installed = NONE
marketplaces known to ltms: ['claude-plugins-official', 'kb-alms',
                             'mgnl-code-review', 'umputun-cc-thingz']

Nobody has it installed, including this fleet, and this repo's marketplace is not even registered. So the rename breaks nothing that exists today — the "breaking change" is theoretical, not a live migration. That makes the rename cheaper than the PR body claims, not more expensive.

It also sharpens the original finding. The plugin was not merely undocumented in the files sessions read: it was never installed anywhere either. It shipped, and then nothing consumed it.

**Correction to the "Breaking for a 0.1.0 install" paragraph.** I wrote "Only this fleet has it installed". I had not measured that. I have now: ``` ltms: fleet/bridge plugins installed = NONE gx10: fleet/bridge plugins installed = NONE marketplaces known to ltms: ['claude-plugins-official', 'kb-alms', 'mgnl-code-review', 'umputun-cc-thingz'] ``` **Nobody has it installed, including this fleet, and this repo's marketplace is not even registered.** So the rename breaks nothing that exists today — the "breaking change" is theoretical, not a live migration. That makes the rename cheaper than the PR body claims, not more expensive. It also sharpens the original finding. The plugin was not merely undocumented in the files sessions read: it was never installed anywhere either. It shipped, and then nothing consumed it.
ltms merged commit 6938f52155 into main 2026-09-05 07:50:19 +02:00
Sign in to join this conversation.