Ship the suite as a Claude Code plugin, replacing the symlink-farm install #22

Closed
opened 2026-08-11 15:50:11 +00:00 by jbr870 · 5 comments
Owner

The problem

Distribution of this suite is a hand-made symlink farm. On this machine:

~/.claude/skills/develop        -> ../repos/devwork-skills/.claude/skills/develop
~/.claude/skills/requirements   -> ../repos/devwork-skills/.claude/skills/requirements
... 15 links, one per skill

That means: every consumer machine repeats the linking by hand; adding or renaming a skill silently needs a manual relink (a missing link is not an error, the skill is just absent); there is no version, no update, no rollback, and no way for a consumer to know their copy is stale.

What we want

Package the suite as a Claude Code plugin, published through a marketplace, so changes made here propagate to everyone who installed it via a normal plugin update instead of a manual ln -s pass.

Points to settle when this is planned

  • Repo layout. Plugin manifest (.claude-plugin/plugin.json) plus where the skills live — keep .claude/skills/ as-is and point the manifest at it, or move to the plugin's expected root.
  • Marketplace hosting. The tracker and canonical remote are Gitea (git.wihslon.com/jbr870/devwork-skills); there is also a GitHub mirror (jbr870/devwork-skills). Marketplace sources are git-URL-shaped — confirm what the installer accepts against the self-hosted Gitea before assuming it works.
  • Executables survive packaging. _shared/procedures/bin/* and each adapter's bin/* are the load-bearing helpers and must arrive with their exec bits intact and their relative paths (_shared/) still resolvable from an installed plugin root. This is the highest-risk item — verify it early with a real install, not by reading docs.
  • One plugin or several. All five forge adapters plus the phase skills as one unit, or a core plugin plus per-adapter plugins so a GitHub shop doesn't carry glab/tea docs.
  • Versioning. A version and a changelog, and how a project pins one — the suite's contracts (sreq:v1, test-plan:v1, Phase Outcome) are consumed by artifacts that outlive an upgrade, so "everybody auto-updates" needs a story for in-flight features on the old contract.
  • Self-hosting. This repo dogfoods its own skills. Once installed as a plugin, the installed copy must not shadow or race the repo's own .claude/skills/ while we're working in here — decide which wins and make it explicit.
## The problem Distribution of this suite is a hand-made symlink farm. On this machine: ``` ~/.claude/skills/develop -> ../repos/devwork-skills/.claude/skills/develop ~/.claude/skills/requirements -> ../repos/devwork-skills/.claude/skills/requirements ... 15 links, one per skill ``` That means: every consumer machine repeats the linking by hand; adding or renaming a skill silently needs a manual relink (a missing link is not an error, the skill is just absent); there is no version, no `update`, no rollback, and no way for a consumer to know their copy is stale. ## What we want Package the suite as a **Claude Code plugin**, published through a marketplace, so changes made here propagate to everyone who installed it via a normal plugin update instead of a manual `ln -s` pass. ## Points to settle when this is planned - **Repo layout.** Plugin manifest (`.claude-plugin/plugin.json`) plus where the skills live — keep `.claude/skills/` as-is and point the manifest at it, or move to the plugin's expected root. - **Marketplace hosting.** The tracker and canonical remote are Gitea (`git.wihslon.com/jbr870/devwork-skills`); there is also a GitHub mirror (`jbr870/devwork-skills`). Marketplace sources are git-URL-shaped — confirm what the installer accepts against the self-hosted Gitea before assuming it works. - **Executables survive packaging.** `_shared/procedures/bin/*` and each adapter's `bin/*` are the load-bearing helpers and must arrive with their exec bits intact and their relative paths (`_shared/`) still resolvable from an installed plugin root. This is the highest-risk item — verify it early with a real install, not by reading docs. - **One plugin or several.** All five forge adapters plus the phase skills as one unit, or a core plugin plus per-adapter plugins so a GitHub shop doesn't carry `glab`/`tea` docs. - **Versioning.** A version and a changelog, and how a project pins one — the suite's contracts (`sreq:v1`, `test-plan:v1`, Phase Outcome) are consumed by artifacts that outlive an upgrade, so "everybody auto-updates" needs a story for in-flight features on the old contract. - **Self-hosting.** This repo dogfoods its own skills. Once installed as a plugin, the installed copy must not shadow or race the repo's own `.claude/skills/` while we're working *in here* — decide which wins and make it explicit.
Author
Owner

Pairs with #21 — see the note there on the shared .claude resolution question.

Pairs with #21 — see the note there on the shared `.claude` resolution question.
Author
Owner

Deployment-lag datapoint for the plugin case: the desktop (jochems-forge) runs the suite via the symlink farm into ~/.claude/repos/devwork-skills, found pinned at ae802e0 (2026-08-12) while main had moved 8 commits (#26 comment-integrity fixes, qa_domains declaration, meta lane). Consequence for process review: "resolved at HEAD" and "deployed where runs happen" are different states, and nothing surfaces the gap — the 2026-08-12 runs validated day-old text only by luck of timing. A plugin (or any update-on-use mechanism) closes exactly this; until then the retrospective has to record the deployed SHA per run to know what text a run actually validated.

Evidence added by /meta-retrospective 2026-08-13.

Deployment-lag datapoint for the plugin case: the desktop (jochems-forge) runs the suite via the symlink farm into `~/.claude/repos/devwork-skills`, found pinned at `ae802e0` (2026-08-12) while main had moved 8 commits (#26 comment-integrity fixes, qa_domains declaration, meta lane). Consequence for process review: "resolved at HEAD" and "deployed where runs happen" are different states, and nothing surfaces the gap — the 2026-08-12 runs validated day-old text only by luck of timing. A plugin (or any update-on-use mechanism) closes exactly this; until then the retrospective has to record the deployed SHA per run to know what text a run actually validated. *Evidence added by /meta-retrospective 2026-08-13.*
Author
Owner

Correction + escalation of my earlier datapoint: the desktop checkout wasn't merely stale-until-pulled — the slot3/#191 deep-dive shows it received 20+ commits including a merge during the 08-11/08-12 runs (one mid-QA commit changed the playbook between stage launches). So the symlink farm doesn't just lag; it lets skill text move under live runs, making version attribution impossible. Filed as #34 (record suite SHA per run / pin per run); a plugin install with explicit versions closes both halves.

Evidence added by /meta-retrospective 2026-08-13.

Correction + escalation of my earlier datapoint: the desktop checkout wasn't merely stale-until-pulled — the slot3/#191 deep-dive shows it received **20+ commits including a merge during the 08-11/08-12 runs** (one mid-QA commit changed the playbook between stage launches). So the symlink farm doesn't just lag; it lets skill text move under live runs, making version attribution impossible. Filed as #34 (record suite SHA per run / pin per run); a plugin install with explicit versions closes both halves. *Evidence added by /meta-retrospective 2026-08-13.*
Author
Owner

Superseded by #50 — closing.

#50 ("Ship the suite as a Claude Code marketplace plugin") took this issue's ask through the full
pipeline and shipped it. It is at phase/accepted as of 2026-08-22, with the plugin released
(dev--v0.1.0dev--v0.1.5) and installed on both machines.

Against this issue's own Points to settle:

  • Repo layout — settled: skills moved to a plugin root (plugin/) with
    .claude-plugin/plugin.json + marketplace.json; shipped skills address each other via
    ${CLAUDE_PLUGIN_ROOT}, and are invoked as /dev:<skill>.
  • Marketplace hosting — settled: GitHub (jbr870/devwork-skills) is the marketplace source.
    Gitea remains the tracker and canonical remote. The installer was verified against a real
    install, not assumed.
  • Executables survive packaging — verified by real install: _shared/procedures/bin/* and the
    adapter bin/* arrive executable and resolve _shared/ from the installed root. This was
    flagged here as the highest-risk item and it held up.
  • One plugin or several — settled: one plugin (dev), all adapters included.
  • Versioning — a version + tag per release ({name}--v{version}), cut by scripts/release.sh
    behind four gates. The in-flight-contract half of this bullet — how a project pins a version and
    what happens to artifacts written against an older contract — was split out as #52 and stays
    open.
  • Self-hosting — settled: the dev machine runs the live checkout as a skills-dir plugin
    (~/.claude/skills/dev -> …/devwork-skills/plugin); the 15-link farm is torn down (0 of 15
    remain). The desktop runs the released version out of the plugin cache.

Still open, deliberately, and tracked separately rather than here:

  • #51 — automated eval suites (plugin eval), out-scoped from #50 by decision.
  • #52 — declared-block schema compatibility (version vs consumer CLAUDE.md declarations).
  • #34 — run-record provenance: nothing stamps the executed suite version/SHA into a Phase
    Outcome. Raised again by #50's UAT (UAT-F33) and out of #50's scope; #34 now carries the
    evidence and the measured design input.

Two things #50 could not deliver, recorded so they are not lost with this issue:

  • A version tag pinned in the marketplace snapshot cannot survive the plugin update/install
    that consumes it (a Claude Code CLI property). Rollback works by re-cutting the previous content
    as a new release, which needs nothing on the consumer.
  • /dev:promote cannot run on this repo — it declares no release: block — so #50 stops at
    accepted. In practice the release already shipped via scripts/release.sh.
**Superseded by #50** — closing. #50 ("Ship the suite as a Claude Code marketplace plugin") took this issue's ask through the full pipeline and shipped it. It is at `phase/accepted` as of 2026-08-22, with the plugin released (`dev--v0.1.0` … `dev--v0.1.5`) and installed on both machines. Against this issue's own **Points to settle**: - **Repo layout** — settled: skills moved to a plugin root (`plugin/`) with `.claude-plugin/plugin.json` + `marketplace.json`; shipped skills address each other via `${CLAUDE_PLUGIN_ROOT}`, and are invoked as `/dev:<skill>`. - **Marketplace hosting** — settled: GitHub (`jbr870/devwork-skills`) is the marketplace source. Gitea remains the tracker and canonical remote. The installer was verified against a real install, not assumed. - **Executables survive packaging** — verified by real install: `_shared/procedures/bin/*` and the adapter `bin/*` arrive executable and resolve `_shared/` from the installed root. This was flagged here as the highest-risk item and it held up. - **One plugin or several** — settled: one plugin (`dev`), all adapters included. - **Versioning** — a version + tag per release (`{name}--v{version}`), cut by `scripts/release.sh` behind four gates. The in-flight-contract half of this bullet — how a project pins a version and what happens to artifacts written against an older contract — was split out as **#52** and stays open. - **Self-hosting** — settled: the dev machine runs the live checkout as a skills-dir plugin (`~/.claude/skills/dev -> …/devwork-skills/plugin`); the 15-link farm is torn down (0 of 15 remain). The desktop runs the released version out of the plugin cache. Still open, deliberately, and tracked separately rather than here: - **#51** — automated eval suites (`plugin eval`), out-scoped from #50 by decision. - **#52** — declared-block schema compatibility (version vs consumer CLAUDE.md declarations). - **#34** — run-record provenance: nothing stamps the executed suite version/SHA into a Phase Outcome. Raised again by #50's UAT (UAT-F33) and out of #50's scope; #34 now carries the evidence and the measured design input. Two things #50 could not deliver, recorded so they are not lost with this issue: - A version tag pinned in the marketplace snapshot **cannot survive** the `plugin update`/`install` that consumes it (a Claude Code CLI property). Rollback works by re-cutting the previous content as a new release, which needs nothing on the consumer. - `/dev:promote` cannot run on this repo — it declares no `release:` block — so #50 stops at `accepted`. In practice the release already shipped via `scripts/release.sh`.
Author
Owner

Linked: this issue is relates_to #50 (recorded by the devwork pipeline).

Linked: this issue is **relates_to** #50 (recorded by the devwork pipeline).
Sign in to join this conversation.
No description provided.