Phase 7 of the Skills + Revisions + Proposals work — documentation pass for the surface added in PR-1 through PR-6. Reference material only; no code changes. ## What's added - `docs/skills.md` — skill model, scoping rules, CLI surface, the `mcpctl config claude --project` setup flow, metadata schema (with the deferred-execution note for hooks/mcpServers/postInstall), the on-disk state file shape, atomic install mechanics, failure semantics, and what's deferred. - `docs/revisions.md` — ResourceRevision model, semver auto-bump rules, contentHash diff key (cross-resource sync), CLI for history / diff / restore, RBAC, audit emission, storage growth note. - `docs/proposals.md` — ResourceProposal model, the reviewer flow (CLI + web UI), atomic-approval mechanics, the propose_prompt / propose_skill MCP tools, the propose-learnings global skill that steers Claude toward engaging with them, and the deferred legacy PromptRequest cutover. ## What's edited - Top-level `CLAUDE.md` — resource cheatsheet adds `skill`, `proposal`, `revision` with cross-references to the new docs. The legacy `promptrequest` entry stays (still on the legacy code path) but notes that new work should use `proposal`. ## What's NOT in this PR - The PromptRequest → ResourceProposal cutover migration. Both run side-by-side today; the focused cutover PR will rename + backfill + drop. Keeping that out of PR-7 means review can stay on docs. - Bundle-backup / `mcpctl apply -f` skill support (deferred from PR-3). - `metadata.hooks` / `metadata.mcpServers` / `metadata.postInstall` execution (deferred from PR-5). - Existing-page UI migration to Tailwind (deferred from PR-6 — old inline-styled pages coexist fine inside the new Layout). These are tracked as future PRs; each is its own focused change. ## Verification `pnpm test:run` whole monorepo: 162 test files / 2157 tests green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
131 lines
4.5 KiB
Markdown
131 lines
4.5 KiB
Markdown
# 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).
|