Proof
Proof - Collaborative Markdown Editor
Section titled “Proof - Collaborative Markdown Editor”Proof is a collaborative document editor for humans and agents. It supports two modes:
- Web API - Create and edit shared documents via HTTP (no install needed)
- Local Bridge - Drive the macOS Proof app via localhost:9847
Identity and Attribution
Section titled “Identity and Attribution”Every write to a Proof doc must be attributed. Two fields carry the agent’s identity:
- Machine ID (
byon every op,X-Agent-Idheader):ai:systematic— stable, lowercase-hyphenated, machine-parseable. Appears in marks, events, and the API response. - Display name (
nameonPOST /presence):Systematic— human-readable, shown in Proof’s presence chips and comment-author badges.
Set the display name once per doc session by posting to presence with the X-Agent-Id header; Proof binds the name to that agent ID for the session. These values are the defaults for any caller of this skill; callers running HITL review (references/hitl-review.md) may pass a different identity pair if a distinct sub-agent should own the doc. Do not use ai:compound or other ad-hoc variants — identity stays uniform unless a caller explicitly overrides it.
Human-in-the-Loop Review Mode
Section titled “Human-in-the-Loop Review Mode”When a caller (e.g., ce-brainstorm, ce-plan) needs to upload a local markdown doc, collect structured human feedback in Proof, and sync the final doc back to disk, load references/hitl-review.md for the full loop spec: invocation contract, mark classification (change / question / objection / ambiguous), idempotent ingest passes, exception-based terminal reporting, and end-sync atomic write.
Web API (Primary for Sharing)
Section titled “Web API (Primary for Sharing)”Create a Shared Document
Section titled “Create a Shared Document”No authentication required. Returns a shareable URL with access token.
curl -X POST https://www.proofeditor.ai/share/markdown \ -H "Content-Type: application/json" \ -d '{"title":"My Doc","markdown":"# Hello\n\nContent here."}'Response format:
{ "slug": "abc123", "tokenUrl": "https://www.proofeditor.ai/d/abc123?token=xxx", "accessToken": "xxx", "ownerSecret": "yyy", "_links": { "state": "https://www.proofeditor.ai/api/agent/abc123/state", "ops": "https://www.proofeditor.ai/api/agent/abc123/ops" }}Use the tokenUrl as the shareable link. The _links give you the exact API paths.
Read a Shared Document
Section titled “Read a Shared Document”curl -s "https://www.proofeditor.ai/api/agent/{slug}/state" \ -H "x-share-token: <token>"Edit a Shared Document
Section titled “Edit a Shared Document”All operations go to POST https://www.proofeditor.ai/api/agent/{slug}/ops
Note: Use the /api/agent/{slug}/ops path (from _links in create response), NOT /api/documents/{slug}/ops.
Authentication for protected docs:
- Header:
x-share-token: <token>orAuthorization: Bearer <token> - Token comes from the URL parameter:
?token=xxxor theaccessTokenfrom create response - Header:
X-Agent-Id: ai:systematic(required for presence; include on ops for consistent attribution)
Wire-format reminder. /api/agent/{slug}/ops uses a top-level type field; /api/agent/{slug}/edit/v2 uses an operations array where each entry has op. Do not mix — sending op to /ops returns 422.
Every mutation requires a baseToken. Read it from /state.mutationBase.token (or /snapshot.mutationBase.token) immediately before each write, and include it in the request body. On BASE_TOKEN_REQUIRED or STALE_BASE, re-read and retry once. See the baseToken recipe in references/hitl-review.md.
Idempotency-Key header is recommended on every mutation for safe automation retries; required when /state.contract.idempotencyRequired is true.
Comment on text:
{"type": "comment.add", "quote": "text to comment on", "by": "ai:systematic", "text": "Your comment here", "baseToken": "<token>"}Reply to a comment:
{"type": "comment.reply", "markId": "<id>", "by": "ai:systematic", "text": "Reply text", "baseToken": "<token>"}Resolve / unresolve a comment:
{"type": "comment.resolve", "markId": "<id>", "by": "ai:systematic", "baseToken": "<token>"}{"type": "comment.unresolve", "markId": "<id>", "by": "ai:systematic", "baseToken": "<token>"}Suggest a replacement (pending — user must accept/reject):
{"type": "suggestion.add", "kind": "replace", "quote": "original text", "by": "ai:systematic", "content": "replacement text", "baseToken": "<token>"}Suggest and immediately apply (tracked but committed — user can reject to revert):
{"type": "suggestion.add", "kind": "replace", "quote": "original text", "by": "ai:systematic", "content": "replacement text", "status": "accepted", "baseToken": "<token>"}status: "accepted" creates the suggestion mark and commits the change in one call. The mark persists as an audit trail with per-edit attribution and a reject-to-revert affordance. Works with kind: "insert" | "delete" | "replace".
Accept or reject an existing suggestion:
{"type": "suggestion.accept", "markId": "<id>", "by": "ai:systematic", "baseToken": "<token>"}{"type": "suggestion.reject", "markId": "<id>", "by": "ai:systematic", "baseToken": "<token>"}suggestion.resolve is not supported — use accept or reject instead.
Bulk rewrite (whole-doc replacement):
{"type": "rewrite.apply", "content": "full new markdown", "by": "ai:systematic", "baseToken": "<token>"}Block-level edits via /edit/v2 (separate endpoint, separate shape):
curl -X POST "https://www.proofeditor.ai/api/agent/{slug}/edit/v2" \ -H "Content-Type: application/json" \ -H "x-share-token: <token>" \ -H "X-Agent-Id: ai:systematic" \ -H "Idempotency-Key: <uuid>" \ -d '{ "by": "ai:systematic", "baseToken": "mt1:<token>", "operations": [ {"op": "replace_block", "ref": "b3", "block": {"markdown": "Updated paragraph."}}, {"op": "insert_after", "ref": "b3", "block": {"markdown": "## New section"}} ] }'Supported op kinds inside operations: replace_block, insert_before, insert_after, delete_block, replace_range (uses fromRef + toRef), find_replace_in_block (takes occurrence: "first" | "all"). Read /snapshot to get stable block ref IDs and the mutationBase.token.
Editing while a client is connected is fine. /edit/v2, suggestion.add (including status: "accepted"), and all comment ops work during active collab. Only rewrite.apply is blocked by LIVE_CLIENTS_PRESENT — it would clobber in-flight Yjs edits.
When the loop breaks. If a mutation keeps failing after a fresh read and one retry, or state across reads looks inconsistent, call POST https://www.proofeditor.ai/api/bridge/report_bug with the failing request ID, slug, and raw response. The server enriches and files an issue.
Known Limitations (Web API)
Section titled “Known Limitations (Web API)”- Bridge-style endpoints (
/d/{slug}/bridge/*) require client version headers (x-proof-client-version,x-proof-client-build,x-proof-client-protocol) and return 426 CLIENT_UPGRADE_REQUIRED without them. Use/api/agent/{slug}/opsinstead.
Local Bridge (macOS App)
Section titled “Local Bridge (macOS App)”Requires Proof.app running. Bridge at http://localhost:9847.
Required headers:
X-Agent-Id: claude(identity for presence)Content-Type: application/jsonX-Window-Id: <uuid>(when multiple docs open)
Key Endpoints
Section titled “Key Endpoints”| Method | Endpoint | Purpose |
|---|---|---|
| GET | /windows | List open documents |
| GET | /state | Read markdown, cursor, word count |
| GET | /marks | List all suggestions and comments |
| POST | /marks/suggest-replace | {"quote":"old","by":"ai:systematic","content":"new"} |
| POST | /marks/suggest-insert | {"quote":"after this","by":"ai:systematic","content":"insert"} |
| POST | /marks/suggest-delete | {"quote":"delete this","by":"ai:systematic"} |
| POST | /marks/comment | {"quote":"text","by":"ai:systematic","text":"comment"} |
| POST | /marks/reply | {"markId":"<id>","by":"ai:systematic","text":"reply"} |
| POST | /marks/resolve | {"markId":"<id>","by":"ai:systematic"} |
| POST | /marks/accept | {"markId":"<id>"} |
| POST | /marks/reject | {"markId":"<id>"} |
| POST | /rewrite | {"content":"full markdown","by":"ai:systematic"} |
| POST | /presence | {"status":"reading","summary":"..."} |
| GET | /events/pending | Poll for user actions |
Presence Statuses
Section titled “Presence Statuses”thinking, reading, idle, acting, waiting, completed
Workflow: Review a Shared Document
Section titled “Workflow: Review a Shared Document”When given a Proof URL like https://www.proofeditor.ai/d/abc123?token=xxx:
- Extract the slug (
abc123) and token from the URL - Read the document state via the API
- Add comments or suggest edits using the ops endpoint
- The author sees changes in real-time
# Readcurl -s "https://www.proofeditor.ai/api/agent/abc123/state" \ -H "x-share-token: xxx"
# Get baseToken for the next mutationBASE=$(curl -s "https://www.proofeditor.ai/api/agent/abc123/state" \ -H "x-share-token: xxx" | jq -r '.mutationBase.token')
# Commentcurl -X POST "https://www.proofeditor.ai/api/agent/abc123/ops" \ -H "Content-Type: application/json" \ -H "x-share-token: xxx" \ -H "X-Agent-Id: ai:systematic" \ -d "$(jq -n --arg base "$BASE" '{type:"comment.add",quote:"text",by:"ai:systematic",text:"comment",baseToken:$base}')"
# Suggest edit (tracked, pending)curl -X POST "https://www.proofeditor.ai/api/agent/abc123/ops" \ -H "Content-Type: application/json" \ -H "x-share-token: xxx" \ -H "X-Agent-Id: ai:systematic" \ -d "$(jq -n --arg base "$BASE" '{type:"suggestion.add",kind:"replace",quote:"old",by:"ai:systematic",content:"new",baseToken:$base}')"
# Suggest and immediately apply (tracked, committed)curl -X POST "https://www.proofeditor.ai/api/agent/abc123/ops" \ -H "Content-Type: application/json" \ -H "x-share-token: xxx" \ -H "X-Agent-Id: ai:systematic" \ -d "$(jq -n --arg base "$BASE" '{type:"suggestion.add",kind:"replace",quote:"old",by:"ai:systematic",content:"new",status:"accepted",baseToken:$base}')"Workflow: Create and Share a New Document
Section titled “Workflow: Create and Share a New Document”# 1. CreateRESPONSE=$(curl -s -X POST https://www.proofeditor.ai/share/markdown \ -H "Content-Type: application/json" \ -d '{"title":"My Doc","markdown":"# Title\n\nContent here."}')
# 2. Extract URL and tokenURL=$(echo "$RESPONSE" | jq -r '.tokenUrl')SLUG=$(echo "$RESPONSE" | jq -r '.slug')TOKEN=$(echo "$RESPONSE" | jq -r '.accessToken')
# 3. Bind display name via presencecurl -s -X POST "https://www.proofeditor.ai/api/agent/$SLUG/presence" \ -H "Content-Type: application/json" \ -H "x-share-token: $TOKEN" \ -H "X-Agent-Id: ai:systematic" \ -d '{"name":"Systematic","status":"reading","summary":"Uploaded doc"}'
# 4. Share the URLecho "$URL"
# 5. Make edits using the ops endpoint (baseToken required)BASE=$(curl -s "https://www.proofeditor.ai/api/agent/$SLUG/state" \ -H "x-share-token: $TOKEN" | jq -r '.mutationBase.token')curl -X POST "https://www.proofeditor.ai/api/agent/$SLUG/ops" \ -H "Content-Type: application/json" \ -H "x-share-token: $TOKEN" \ -H "X-Agent-Id: ai:systematic" \ -d "$(jq -n --arg base "$BASE" '{type:"comment.add",quote:"Content here",by:"ai:systematic",text:"Added a note",baseToken:$base}')"Workflow: Pull a Proof Doc to Local
Section titled “Workflow: Pull a Proof Doc to Local”Sync the current Proof doc state to a local markdown file. Used by:
- HITL review end-sync (
references/hitl-review.mdPhase 5) when the doc originated from a local file - Ad-hoc snapshots of a Proof doc to disk (before closing the tab, archiving, handing off)
- Refreshing a local working copy against the live Proof version
SLUG=<slug>TOKEN=<accessToken>LOCAL=<absolute-path>
# One read to a temp file — avoids passing markdown through $(...), which would strip trailing newlines.STATE_TMP=$(mktemp)curl -s "https://www.proofeditor.ai/api/agent/$SLUG/state" \ -H "x-share-token: $TOKEN" > "$STATE_TMP"REVISION=$(jq -r '.revision' "$STATE_TMP")
# Atomic write: stream .markdown bytes directly to a temp sibling, then rename.TMP="${LOCAL}.proof-sync.$$"jq -jr '.markdown' "$STATE_TMP" > "$TMP" && mv "$TMP" "$LOCAL"rm "$STATE_TMP"jq -jr (-j no trailing newline, -r raw string) streams the markdown bytes straight to the temp file without going through a shell variable, so trailing newlines survive intact. mv within the same filesystem is atomic — a crashed write leaves the original untouched rather than a half-written file.
Confirm before writing when the pull isn’t directly asked for. If a workflow ends up pulling as a side-effect of a different action (e.g., HITL review completion), surface the impending write with a short confirm like “Sync reviewed doc to <localPath>?” A silent overwrite is surprising — the user may have forgotten the local file exists in that session, or expected Proof to stay canonical until they explicitly asked to pull.
Safety
Section titled “Safety”- Use
/statecontent as source of truth before editing - During active collab use
edit/v2(direct block changes) orsuggestion.add(tracked changes); reserverewrite.applyfor no-client scenarios since it’s blocked byLIVE_CLIENTS_PRESENTwhen anyone is connected - Don’t span table cells in a single replace
- Always include
by: "ai:systematic"on every op andX-Agent-Id: ai:systematicin headers for consistent attribution - Read a fresh
baseTokenbefore every mutation; onSTALE_BASE, re-read and retry once