Files
mcpctl/docs/proposals.md
Michal 56735a5290 docs: skills + revisions + proposals reference, plus cheatsheet update
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>
2026-05-07 17:58:04 +01:00

127 lines
5.3 KiB
Markdown

# Resource Proposals
A proposal is a pending change to a Prompt or Skill, submitted by
either a Claude Code session (via the `propose_prompt` / `propose_skill`
MCP tools) or a human (via the web UI / CLI). Reviewers triage the
queue and either approve — at which point the proposal becomes a real
prompt or skill — or reject with a note.
This is the path by which Claude **proposes back** to mcpd: things the
session learned that future sessions would benefit from. The
`propose-learnings` global skill (seeded by mcpd at startup) explains
the discipline to Claude.
## Model
`ResourceProposal` shares the schema's discriminator pattern with
`ResourceRevision` — single table, `resourceType` field disambiguates
prompts vs skills.
| Field | Purpose |
|----------------------|--------------------------------------------------------|
| `resourceType` | `'prompt'` \| `'skill'`. |
| `name` | Proposed resource name. |
| `body` | Proposed body (`{ content, priority?, metadata?, … }`).|
| `projectId` / `agentId` | Scope of the proposal (XOR; null/null = global). |
| `createdBySession` | mcplocal session that proposed (when from Claude). |
| `createdByUserId` | User who proposed (when via UI/CLI). |
| `status` | `'pending'``'approved'` \| `'rejected'`. |
| `reviewerNote` | Set on approval or rejection. |
| `approvedRevisionId` | Set when approved — points at the resulting revision. |
Two unique constraints — `(resourceType, name, projectId)` and
`(resourceType, name, agentId)` — mirror the Prompt / Skill scoping
rules. The same `?? ''` workaround for nullable-FK lookups applies.
## Reviewer flow
### CLI
```bash
mcpctl review pending # list pending
mcpctl review next # show oldest pending
mcpctl review show <id> # full detail
mcpctl review diff <id> # before/after diff
mcpctl review approve <id> # POST /proposals/:id/approve
mcpctl review reject <id> --reason "explain" # rejected with note
```
### Web UI
`/proposals` shows a Pending / Approved / Rejected tab view; the
sidebar nav badge polls every 30 s and shows the pending count in
amber. Click a row to see the full body, the diff against the current
resource (if any), and approve / reject controls.
### Approval is atomic
Approval runs in a single Prisma transaction:
1. Look up the pending proposal.
2. Dispatch by `resourceType` to the registered handler
(`PromptService` or `SkillService` registers itself at construction).
3. Handler upserts the underlying resource — creating it if new, or
updating + auto-bumping patch semver if it exists.
4. Handler records a `ResourceRevision` linking back to the proposal.
5. Proposal status flips to `approved`, `approvedRevisionId` set.
If any step fails, the transaction rolls back and the proposal stays
`pending`. There is no half-approved state.
## Claude side: `propose_prompt` and `propose_skill`
Both tools are registered by the `gate` plugin in mcplocal. They post
to `/api/v1/proposals` with the appropriate `resourceType`.
The `propose-learnings` global skill (seeded by mcpd) tells Claude
*when* to use them:
- `propose_prompt` for project-specific knowledge — gotchas,
conventions, hidden constraints. Cheap to add, easy to reject.
- `propose_skill` for cross-cutting knowledge — debugging discipline,
release hygiene, security review style. Larger blast radius; lean
toward `propose_prompt` unless you have a clear cross-project reason.
The `gate-encouragement-propose` system prompt (priority 10, sits in
the gating bundle) is the trigger that makes Claude actually consider
proposing. Without that, the tools exist but Claude rarely engages.
## Backwards compat
PR-1 / PR-2 deferred the cutover from the prompt-only `PromptRequest`
table to `ResourceProposal`. Both run side-by-side today:
- mcplocal's `propose_prompt` still POSTs to the legacy
`/api/v1/projects/:name/promptrequests` URL.
- mcplocal's `propose_skill` (newer) POSTs to `/api/v1/proposals`
directly.
- The legacy `/api/v1/promptrequests*` routes remain in mcpd.
- `mcpctl approve promptrequest <name>` still works.
A focused follow-up PR will:
1. Migrate existing `PromptRequest` rows into `ResourceProposal`
(resourceType=prompt).
2. Rename `PromptRequest` to `_PromptRequest_legacy`.
3. Update mcplocal's `propose_prompt` to use `/api/v1/proposals`.
4. Keep the legacy URL as a thin translation shim through one release.
5. Drop `_PromptRequest_legacy` after that.
This stays separate so the cutover is reviewable independently of
the larger Skills + Revisions + Proposals work.
## RBAC
Proposals piggyback on the `prompts` permission for now — anyone with
`view:prompts` can read the queue, anyone with `edit:prompts` can
approve or reject. Splitting out a dedicated `proposals` permission
(or a "reviewer" role) is straightforward if granularity becomes
useful.
## Audit emission
Proposal create / approve / reject events flow through the existing
audit pipeline. Approval events also reference the resulting
revision id, so you can join "proposal approved at T" against
"revision X created at T" without polling.