# Releasing the-ai-machine

This document describes how to publish a new version of the marketplace without breaking consumers.

## Model

A single branch (`main`), one shared tag per release, multiple plugins inside `plugins/`. Two release modes share the same tag namespace.

| Ref            | Role                                                                                          |
|----------------|-----------------------------------------------------------------------------------------------|
| `main`         | Day-to-day work for the whole team. `.claude-plugin/marketplace.json` declares the tag to install per plugin. |
| `vX.Y.Z` (tag) | Immutable snapshot at release time. Every plugin's `source.ref` points at the tag of the release that last touched it. |

### Versioning invariant

**Every plugin shares the same `major.minor`.** Patch values may differ — a plugin that gets a quick hotfix advances its patch without dragging the others along.

`scripts/release.sh` enforces this: it aborts if `major.minor` drifts between any two plugins. To rebump `major` or `minor` you must do a global release that moves all plugins together.

### Two release modes

**Global release** — every plugin bumps to the same new version, and every `source.ref` moves to the new tag.

- `scripts/release.sh patch`  →  `max(patches) + 1` across all plugins, then everyone aligns to that new patch.
- `scripts/release.sh minor`  →  `(minor + 1).0` across all plugins.
- `scripts/release.sh major`  →  `(major + 1).0.0` across all plugins.
- `scripts/release.sh X.Y.Z`  →  explicit version, must be ahead of every plugin.

A global release "re-synchronizes" any divergence introduced by previous per-plugin releases.

**Per-plugin release** — only the named plugin bumps; the other plugins' `source.ref` entries are untouched, so consumers do not redownload them.

- `scripts/release.sh --plugin <name> patch`  →  only `<name>` bumps its patch by 1.

Only `patch` is allowed in per-plugin mode. Major/minor changes affect every plugin's compatibility surface and must go through a global release.

## Marketplace source schema

Each entry in `plugins[]` of `.claude-plugin/marketplace.json` uses `git-subdir`:

```json
"source": {
  "source": "git-subdir",
  "url": "git@gitlab.yareytech.com:the-ai-machine/the-ai-machine.git",
  "path": "plugins/<plugin-name>",
  "ref": "vX.Y.Z"
}
```

When a consumer refreshes, Claude Code:

1. Reads `marketplace.json` from the HEAD of `main`.
2. For every plugin, takes its `source.ref` + `source.path`.
3. Clones the repo at that ref and reads the plugin from `plugins/<plugin-name>/`.

So **a commit on `main` that does NOT modify `source.ref` is invisible to consumers.** Only when the release script advances the `ref` do consumers receive new code.

## Hard rule

Nobody edits `.claude-plugin/marketplace.json` by hand. The only way to move `source.ref` is via `scripts/release.sh`.

## Where versions live

SemVer per plugin. The version of each plugin appears in:

- `.claude-plugin/marketplace.json` — every `plugins[].version` (and `metadata.version`, which tracks the most recent release tag).
- Every `plugins/*/.claude-plugin/plugin.json` — `version`.

What each part of the version means:

- **patch** — bugfix in a plugin's skill or command.
- **minor** — new skill, new command, new plugin, or backwards-compatible improvement.
- **major** — breaking change to a plugin's interface.

## Release flow

You are on `main`, working tree clean.

```bash
# Global release — every plugin bumps and resyncs.
scripts/release.sh patch          # or: minor | major | 0.3.0

# Per-plugin release — only that plugin bumps.
scripts/release.sh --plugin machine-business patch
```

Inspect the result:

```bash
git show HEAD
git show vX.Y.Z -- .claude-plugin/marketplace.json
git show vX.Y.Z -- plugins/*/.claude-plugin/plugin.json
```

Publish:

```bash
git push origin main
git push origin vX.Y.Z
```

## What `scripts/release.sh` does

Both modes follow the same skeleton; the difference is whose `plugin.json` is touched and whose entry in `marketplace.json` is repointed.

1. Verifies you are on `main` with a clean working tree.
2. Discovers every plugin manifest under `plugins/*/.claude-plugin/plugin.json`.
3. Validates the `major.minor` invariant across plugins (aborts if drifted).
4. Computes the next version:
   - Global: bump applied to the highest current version across all plugins.
   - Per-plugin: bumps only the named plugin's patch.
5. In a single commit:
   - Rewrites `version` in each affected `plugins/<name>/.claude-plugin/plugin.json`.
   - Updates `metadata.version` in `marketplace.json` to the new tag (always; it tracks "last release").
   - Updates each affected `plugins[].version` and `plugins[].source.ref`.
   - Plugins not affected keep their existing `version` and `source.ref` — consumers do not redownload them.
6. Creates the annotated tag `vX.Y.Z`.
7. Prints the `git push` commands.

The script **does not push**.

## Adding a new plugin to the marketplace

1. Create `plugins/<new-plugin>/` with at least:
   - `.claude-plugin/plugin.json` containing `name`, `version` matching the current shared `major.minor` (you can start the patch at any value — `0` is fine), and `description`.
   - The plugin's component folders (`skills/`, `commands/`, `agents/`, `hooks/` — whatever applies).
2. Add an entry to `.claude-plugin/marketplace.json`:
   ```json
   {
     "name": "<new-plugin>",
     "source": {
       "source": "git-subdir",
       "url": "<the same url as the others>",
       "path": "plugins/<new-plugin>",
       "ref": "<any existing tag — release.sh will repoint it>"
     },
     "description": "...",
     "version": "<must match the plugin.json above>",
     ...
   }
   ```
3. Commit the addition on `main`. The new plugin is **not visible to consumers yet** — its `ref` points at a tag where its folder does not exist.
4. Run `scripts/release.sh patch` (or `minor` if it warrants it). For the first release of a new plugin, prefer the global form so its `source.ref` lands on a tag that actually contains its folder. After pushing main + the new tag, the plugin is installable.

## Hotfix on an already-published release

If you need to patch `v0.2.0` without dragging in what is on `main`:

```bash
git checkout -b hotfix/v0.2.1 v0.2.0
# ... fix the bug, commit ...
scripts/release.sh patch       # bumps 0.2.0 -> 0.2.1 on the hotfix branch
git checkout main
git merge --no-ff hotfix/v0.2.1   # or cherry-pick if you prefer
git push origin main
git push origin v0.2.1
git branch -d hotfix/v0.2.1
```

## Install instructions for consumers

```
/plugin marketplace add git@gitlab.yareytech.com:the-ai-machine/the-ai-machine.git
/plugin install <plugin-name>@the-ai-machine
```

Updates propagate by running:

```
/plugin marketplace update the-ai-machine
/plugin update <plugin-name>
```

## Pre-release checklist

- [ ] Working tree clean on `main`.
- [ ] CI green (once it exists).
- [ ] `CHANGELOG.md` updated with the new version and the changes.
- [ ] New or modified skills follow the conventions in `CLAUDE.md`.
- [ ] `README.md` reflects any new skill, command, or plugin.
- [ ] Diff reviewed: `git diff vX.Y.Z..HEAD -- plugins`.
