# Resource Revisions

mcpctl keeps an append-only revision log for every Prompt and Skill —
so you can answer "who changed prompt X and when," diff between any
two versions, and restore an earlier state without losing the audit
chain.

## Model

`ResourceRevision` is a single shared table keyed by
`(resourceType, resourceId)` — the type discriminator allows the same
infrastructure to cover both prompts and skills (and any future
resource that wants version history).

| Field            | Purpose                                                  |
|------------------|----------------------------------------------------------|
| `id`             | cuid; the revision's stable identity.                    |
| `resourceType`   | `'prompt'` \| `'skill'`. Validated app-layer.            |
| `resourceId`     | Soft FK — survives deletion of the underlying resource.  |
| `semver`         | Author-visible version (X.Y.Z).                          |
| `contentHash`    | sha256 of the canonicalised body. Stable diff key.        |
| `body`           | Snapshot of the resource at this revision.              |
| `authorUserId`   | Who made the change (null for system writes).            |
| `authorSessionId`| Session that proposed it (when applicable).              |
| `note`           | Free-text reviewer or author note.                       |
| `createdAt`      | When the revision was recorded.                          |

The resource row itself (Prompt/Skill) keeps the inline `content` —
revisions are an audit log, not the source of truth. Hot read paths
(the gate plugin, `mcpctl skills sync`, prompt indexing) never need
to consult the revision log.

`Prompt.currentRevisionId` and `Skill.currentRevisionId` are soft
pointers to the latest revision so the UI can answer "which version is
live" in one query.

## Semver semantics

Auto-patch on every successful save where the body changed:

```
0.1.0 → save with content change → 0.1.1
0.1.1 → save with content change → 0.1.2
```

Authors can override:

```bash
mcpctl edit prompt foo --bump minor              # 0.1.x → 0.2.0
mcpctl edit prompt foo --bump major              # 0.x.x → 1.0.0
mcpctl edit prompt foo --semver 1.2.3            # explicit
mcpctl edit prompt foo --note "fixed the gotcha" # adds note to revision
```

Invalid semver values fall back to `0.1.0` rather than throwing —
the revision write is best-effort and we don't want a corrupted
existing semver to break the prompt save.

## contentHash

sha256 of the JSON-canonicalised body (keys sorted at every object
level). Two revisions with the same hash are byte-identical. Used by
`mcpctl skills sync` as the diff key against on-disk state — re-publish
under the same semver still triggers a sync if the contentHash changed.

The server-side hash and the client-side hash are computed from the
same canonical shape, so they match exactly. See
`src/mcpd/src/services/resource-revision.service.ts` for the canonical
JSON encoder.

## CLI

### View history

```bash
mcpctl get revisions prompt my-prompt
mcpctl get revisions skill demo-skill
```

### View one

```bash
mcpctl describe revision <id>
```

### Diff

The HTTP API returns a unified-format diff:

```
GET /api/v1/revisions/<id>/diff?against=<other-id|live>
```

The web UI's revision history tab on a Skill detail page renders the
diff inline (color-coded add/remove rows).

### Restore

Restore a prompt or skill to an earlier revision. This writes a *new*
revision whose body is the old one — preserving the audit chain
rather than deleting later revisions.

```bash
mcpctl restore prompt my-prompt --revision <revision-id>
```

The CLI subcommand is wired through to `POST
/api/v1/prompts/:id/restore-revision` (and the symmetric
`/api/v1/skills/:id/restore-revision`).

## RBAC

Revisions piggyback on the underlying resource's RBAC permission. If
you can `view:prompts`, you can read prompt history; if you can
`edit:prompts`, you can restore.

## Audit emission

Each revision write emits a structured audit event captured by the
existing audit-event pipeline. The event includes the revision id,
contentHash, semver, and author/session — sufficient to answer "what
changed" and "who" without joining tables manually.

## Storage size

A revision body is the resource snapshot — for prompts that's a few
KB; for skills with large `files` maps it can be tens of KB. The audit
log grows linearly with edits. v1 has no rotation; if a single resource
sees thousands of revisions per day this will need a retention policy
(out of scope today).
