PortReeve MCP · Slice I-4

Preview first. Execute only while the evidence still agrees.

PR #46 gives destructive and configuration-changing MCP operations a durable, five-minute safety handshake. The model can inspect exactly what would happen; the daemon remains the authority that decides whether it still may happen.

Why this slice exists

The risk

A preview can become false seconds later when a listener, claim, file, setting, or stack changes.

The boundary

The MCP process never gets authority from a remembered answer. It presents a receipt back to the daemon.

The result

Seven consequential action families are now inspectable, stale-safe, explicit, and retryable.

The core transaction

1. Preview The caller supplies an explicit target and proposed change. PortReeve records fresh evidence and returns a receipt.
2. Decide The agent or human reads a typed impact summary. The receipt expires after five minutes.
3. Execute The caller sends only the receipt and explicit target. The daemon reloads the proposal and recomputes evidence.
If evidence changed, execution stops. If this receipt already completed, PortReeve replays the persisted result before looking at today's world. That makes retries safe without turning old authority into new authority.

What is covered

FamilyPreview bindsExecute can do
Portlistener identity and reclamation evidencereclaim an eligible owned listener
Claimsclaim rows, ownership, worktree, and prune candidatesreassign, delete, or prune claims
Stack registrydefinition, activations, launchers, and prune candidatesapply a definition or prune stacks
Settingscurrent settings fingerprint and desired patchapply validated settings changes
Stack documentcanonical path, validated structure, and byte fingerprintcreate or safely replace the fixed document

Document safety becomes one policy

The CLI/server and Desktop previously risked evolving separate definitions of a safe stack document. This slice introduces shared primitives for the fixed portreeve.stack.json file.

resolve canonical existing directory
  → inspect the fixed filename without following a link
  → require a regular file and at most 1 MiB
  → parse and validate a typed stack definition
  → fingerprint the exact bytes
  → compare the expected fingerprint before atomic replace

MCP reads return the parsed definition and fingerprint, not arbitrary file contents. The Desktop adapter now delegates to these same primitives, retaining its overwrite/cancel behavior.

Important code paths

Review conclusions

Passed

453 tests, typecheck, lint, changed-file formatting, documentation checks, and standalone build.

Intentionally absent

No raw file tool, no generic JSON escape hatch, no project shell execution, and no unsafe any-owner eviction.

Still deferred

Docker snapshots and launcher coordination arrive in I-5; real host evidence-change cases remain I-7.

Check your understanding

1. Why does execute accept a receipt and explicit target, but not the proposal?
The daemon reloads the exact persisted proposal, preventing the caller from changing the approved operation between preview and execute.
2. What happens when live evidence differs from preview evidence?
An incomplete receipt is rejected as stale and the target mutation is not invoked.
3. Why is completed replay checked before current evidence?
A transport retry must return the already-achieved result even if the world changed afterward; it must not attempt the mutation again.
4. Can MCP read any project file through the stack-document tool?
No. The tool is restricted to the fixed stack document, returns validated structured data, rejects links/nonregular files, and enforces a size bound.
5. Does previewing a port reclaim send a signal or write an audit event?
No. Preview observes evidence only. The consequential effect belongs exclusively to receipt-backed execute.