
Ce Proof
- 1 installs
- 23.9k repo stars
- Updated August 5, 2026
- everyinc/compounding-engineering-plugin
This is a copy of ce-proof by everyinc - installs and ranking accrue to the original listing.
Helps with ai & agent building tasks.
About
ce-proof is a Claude Code skill for ai & agent building. It helps you ship faster with AI-assisted development.
- ce-proof
- AI & Agent Building
- AI-coding skill
Ce Proof by the numbers
- 1 all-time installs (skills.sh)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/everyinc/compounding-engineering-plugin --skill ce-proofAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 23.9k |
| Last updated | August 5, 2026 |
| Repository | everyinc/compounding-engineering-plugin ↗ |
What it does
Helps with ai & agent building tasks.
Files
Proof - Collaborative Markdown Editor
Proof is a collaborative document editor for humans and agents. It supports two modes:
1. Web API - Create and edit shared documents via HTTP (no install needed) 2. Local Bridge - Drive the macOS Proof app via localhost:9847
Identity and Attribution
Every write to a Proof doc must be attributed. Two fields carry the agent's identity:
- Machine ID (`by` on every op, `X-Agent-Id` header):
ai:compound-engineering— stable, lowercase-hyphenated, machine-parseable. Appears in marks, events, and the API response. - Display name (`name` on `POST /presence`):
Compound Engineering— 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
Human-in-the-loop iteration over an existing local markdown file: upload to Proof, let the user annotate in Proof's web UI, ingest feedback as in-thread replies and agreed edits, and sync the final doc back to disk. Two entry points, identical mechanics — load references/hitl-review.md for the full loop spec (invocation contract, mark classification, idempotent ingest passes, exception-based terminal reporting, end-sync atomic write) in either case:
- Direct user request — a bare user phrase naming a local markdown file and asking to iterate collaboratively via Proof: "share this to proof so we can iterate", "iterate with proof on this doc", "HITL this file with me", "let's get feedback on this in proof", "open this in proof editor so I can review". The file is whichever markdown the user just created, edited, or referenced; if ambiguous, ask which file. This is a first-class entry point — do not require an upstream caller.
- Upstream skill handoff —
ce-brainstorm,ce-ideate, orce-planfinishes a draft and hands it off for human review before the next phase, passing the file path and title explicitly.
Web API (Primary for Sharing)
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
If you already have a shared Proof URL, no browser automation is needed. Fetch the URL directly with content negotiation:
curl -s -H "Accept: application/json" "https://www.proofeditor.ai/d/{slug}?token=<token>"
curl -s -H "Accept: text/markdown" "https://www.proofeditor.ai/d/{slug}?token=<token>"The JSON response includes the markdown, API links, and agent auth hints. Use /state when you need mutation metadata, marks, or presence:
curl -s "https://www.proofeditor.ai/api/agent/{slug}/state" \
-H "x-share-token: <token>"For comment-ingest workflows, prefer the server-side filter:
curl -s "https://www.proofeditor.ai/api/agent/{slug}/state?kinds=comment" \
-H "x-share-token: <token>"state.marks is a union of comments, suggestions, and provenance/authorship marks. The ?kinds=comment filter avoids treating human-authored provenance marks as review comments.
Edit a Shared Document
Comment, suggestion, and rewrite operations go to POST https://www.proofeditor.ai/api/agent/{slug}/ops. Block edits use /api/agent/{slug}/edit/v2.
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:compound-engineering(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`. Reuse the mutationBase.token from the most recent /state or /snapshot read, then update it from successful mutation responses (.mutationBase.token). On BASE_TOKEN_REQUIRED or STALE_BASE, re-read and retry once. Only do a pre-mutation read if no prior read has happened in this session or you need fresh document/comment/snapshot content. See the baseToken recipe in references/hitl-review.md.
/edit/v2 block refs are a separate concern: they can drift across revisions, so re-fetch /snapshot for fresh refs before a block edit if any writes have landed since your last snapshot.
Edit Strategy: Avoid Whole-Doc Rewrite
Do not default to full-document replacement. Pick the narrowest edit primitive that matches the requested change:
1. Literal repeated change: use /edit/v2 with find_replace_in_doc (optionally constrained by fromRef, toRef, or block_filter). This is the fastest and least error-prone path for terminology renames, punctuation/style sweeps, and other exact text substitutions. 2. Known block or section change: use /edit/v2 block operations from a fresh /snapshot: replace_block, insert_before, insert_after, delete_block, replace_range, or find_replace_in_block. 3. Visible track-changes desired: use /ops suggestion.add (pending or status: "accepted") when the user should see a suggestion mark and reject/revert affordance for that specific edit. 4. Whole-doc replacement: use rewrite.apply only as a last resort when the user explicitly asks to replace the entire document, when the intended change is genuinely global and cannot be expressed as block/range/find-replace operations, and when no live clients are present. Before rewriting, read current state, preserve comments/marks expectations, and mention that the rewrite is broad.
When in doubt, start with /snapshot and build a small /edit/v2 batch. A narrow failed edit is easier to inspect and retry than a broad rewrite, and it avoids clobbering concurrent human work.
Retry discipline after mutation errors — verify before retrying. An error response is not proof that nothing was written.
STALE_BASE,BASE_TOKEN_REQUIRED,MISSING_BASE,INVALID_BASE_TOKEN— pre-commit, token-related. Re-read/state, rebuild the request body with a freshbaseToken, and retry once with a newIdempotency-Key.ANCHOR_NOT_FOUND,ANCHOR_AMBIGUOUS— pre-commit, but thequoteno longer uniquely matches content. Re-reading does not help by itself; the caller must tighten or regenerate the anchor before retrying. Do not auto-retry blindly.INVALID_OPERATIONS,INVALID_REQUEST,INVALID_REF,INVALID_BLOCK_MARKDOWN,INVALID_RANGE,INVALID_MARKDOWN, 422 — pre-commit, but the payload is wrong. Do not retry blindly; fix the payload first.COLLAB_SYNC_FAILED,REWRITE_BARRIER_FAILED,PROJECTION_STALE,INTERNAL_ERROR, 5xx, network timeout, and any 202 with `collab.status: "pending"` — the canonical doc may have been written even though the call looks like a failure. Before any retry, re-read/stateand check whether the intended mark/edit is already present; only retry if it isn't.Idempotency-Key(see below) protects against double-apply on the same request (e.g., TCP-level retry). It does not help if you build a new request body and send a second call — that is a new logical write with a new key.
Duplicate-mark incidents usually come from retrying a comment.add or suggestion.add after a timeout without verifying. When in doubt: re-read, diff, then decide.
`Idempotency-Key` header is recommended on every mutation for safe automation retries; required when /state.contract.idempotencyRequired is true. Use the same key only when resending the exact same serialized request body. If the body changes — including because you replaced baseToken after STALE_BASE — mint a new key or Proof will reject it as key reuse with a different payload.
Comment on text:
{"type": "comment.add", "quote": "text to comment on", "by": "ai:compound-engineering", "text": "Your comment here", "baseToken": "<token>"}Reply to a comment:
{"type": "comment.reply", "markId": "<id>", "by": "ai:compound-engineering", "text": "Reply text", "baseToken": "<token>"}Reply and resolve in one mutation:
{"type": "comment.reply", "markId": "<id>", "by": "ai:compound-engineering", "text": "Fixed.", "resolve": true, "baseToken": "<token>"}Batch existing-thread comment mutations:
{"by": "ai:compound-engineering", "baseToken": "<token>", "operations": [
{"type": "comment.reply", "markId": "<id-1>", "text": "Fixed.", "resolve": true},
{"type": "comment.reply", "markId": "<id-2>", "text": "Leaving this open because X."}
]}Batch /ops supports comment.reply, comment.resolve, and comment.unresolve for existing threads. Use it for HITL ingest passes instead of issuing separate reply and resolve requests per thread.
Resolve / unresolve a comment:
{"type": "comment.resolve", "markId": "<id>", "by": "ai:compound-engineering", "baseToken": "<token>"}
{"type": "comment.unresolve", "markId": "<id>", "by": "ai:compound-engineering", "baseToken": "<token>"}Suggest a replacement (pending — user must accept/reject):
{"type": "suggestion.add", "kind": "replace", "quote": "original text", "by": "ai:compound-engineering", "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:compound-engineering", "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:compound-engineering", "baseToken": "<token>"}
{"type": "suggestion.reject", "markId": "<id>", "by": "ai:compound-engineering", "baseToken": "<token>"}suggestion.resolve is not supported — use accept or reject instead.
Whole-doc rewrite (last resort):
{"type": "rewrite.apply", "content": "full new markdown", "by": "ai:compound-engineering", "baseToken": "<token>"}Prefer find_replace_in_doc or block-level /edit/v2 operations first. rewrite.apply is broad, disruptive, and blocked while live clients are connected.
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:compound-engineering" \
-H "Idempotency-Key: <uuid>" \
-d '{
"by": "ai:compound-engineering",
"baseToken": "mt1:<token>",
"operations": [
{"op": "replace_block", "ref": "b3", "block": {"markdown": "Updated paragraph."}},
{"op": "insert_after", "ref": "b3", "blocks": [{"markdown": "## New section"}]}
]
}'Per-op body shape (singular block vs plural blocks is load-bearing — sending the wrong one returns 422):
| op | body fields |
|---|---|
replace_block | ref, block: {markdown} |
insert_after | ref, blocks: [{markdown}, ...] |
insert_before | ref, blocks: [{markdown}, ...] |
delete_block | ref |
replace_range | fromRef, toRef, blocks: [{markdown}, ...] |
find_replace_in_block | ref, find, replace, `occurrence: "first" \ |
find_replace_in_doc | find, replace, `occurrence: "first" \ |
Read /snapshot to get block ref IDs and mutationBase.token. ref values are opaque request tokens tied to the snapshot/baseToken; re-read /snapshot before follow-up block edits if writes have landed. operations commits atomically — either every op lands or none do — so one /edit/v2 call can batch dozens of block edits safely and efficiently (see the bulk-sweep guidance in references/hitl-review.md Phase 2.4). Successful full responses include the next mutationBase.token and fresh snapshot.blocks[].ref values for chaining.
For literal doc-wide sweeps, prefer find_replace_in_doc over many block replacements or a whole-doc rewrite. Validate large batches with ?dryRun=1 or ?validate=1; use ?return=minimal when you only need ok, revision, appliedCount, and the next mutationBase.
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)
- 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)
Requires Proof.app running. Bridge at http://localhost:9847.
Required headers:
X-Agent-Id: ai:compound-engineering(identity for presence; keep aligned withby)Content-Type: application/jsonX-Window-Id: <uuid>(when multiple docs open)
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:compound-engineering","content":"new"} |
| POST | /marks/suggest-insert | {"quote":"after this","by":"ai:compound-engineering","content":"insert"} |
| POST | /marks/suggest-delete | {"quote":"delete this","by":"ai:compound-engineering"} |
| POST | /marks/comment | {"quote":"text","by":"ai:compound-engineering","text":"comment"} |
| POST | /marks/reply | {"markId":"<id>","by":"ai:compound-engineering","text":"reply"} |
| POST | /marks/resolve | {"markId":"<id>","by":"ai:compound-engineering"} |
| POST | /marks/accept | {"markId":"<id>"} |
| POST | /marks/reject | {"markId":"<id>"} |
| POST | /rewrite | Last-resort whole-doc replacement: {"content":"full markdown","by":"ai:compound-engineering"} |
| POST | /presence | {"status":"reading","summary":"..."} |
| GET | /events/pending | Poll for user actions |
Presence Statuses
thinking, reading, idle, acting, waiting, completed
Workflow: Review a Shared Document
When given a Proof URL like https://www.proofeditor.ai/d/abc123?token=xxx:
1. Extract the slug (abc123) and token from the URL 2. Read the document via content negotiation on the shared URL or via /api/agent/{slug}/state when you need marks/mutation metadata 3. For content edits, prefer /edit/v2 find_replace_in_doc or block operations; use /ops for comments, suggestions, and comment replies/resolution 4. The author sees changes in real-time
SHARE_URL="https://www.proofeditor.ai/d/abc123?token=xxx"
curl -s -H "Accept: application/json" "$SHARE_URL"
curl -s -H "Accept: text/markdown" "$SHARE_URL"
# Read once for content + the initial baseToken.
# After each successful mutation, update BASE from the response's mutationBase.token.
STATE=$(curl -s "https://www.proofeditor.ai/api/agent/abc123/state" \
-H "x-share-token: xxx")
BASE=$(printf '%s' "$STATE" | jq -r '.mutationBase.token')
# Inspect doc fields as needed: printf '%s' "$STATE" | jq '.markdown, .revision'
# Comment
OP_RESP=$(curl -s -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:compound-engineering" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg base "$BASE" '{type:"comment.add",quote:"text",by:"ai:compound-engineering",text:"comment",baseToken:$base}')")
NEXT_BASE=$(printf '%s' "$OP_RESP" | jq -r '.mutationBase.token // empty')
[ -n "$NEXT_BASE" ] && BASE="$NEXT_BASE"
# Suggest edit (tracked, pending)
OP_RESP=$(curl -s -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:compound-engineering" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg base "$BASE" '{type:"suggestion.add",kind:"replace",quote:"old",by:"ai:compound-engineering",content:"new",baseToken:$base}')")
NEXT_BASE=$(printf '%s' "$OP_RESP" | jq -r '.mutationBase.token // empty')
[ -n "$NEXT_BASE" ] && BASE="$NEXT_BASE"
# Suggest and immediately apply (tracked, committed)
OP_RESP=$(curl -s -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:compound-engineering" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg base "$BASE" '{type:"suggestion.add",kind:"replace",quote:"old",by:"ai:compound-engineering",content:"new",status:"accepted",baseToken:$base}')")
NEXT_BASE=$(printf '%s' "$OP_RESP" | jq -r '.mutationBase.token // empty')
[ -n "$NEXT_BASE" ] && BASE="$NEXT_BASE"
# Direct content edit (preferred when visible suggestion marks are not needed)
SNAPSHOT=$(curl -s "https://www.proofeditor.ai/api/agent/abc123/snapshot" \
-H "x-share-token: xxx")
EDIT_BASE=$(printf '%s' "$SNAPSHOT" | jq -r '.mutationBase.token')
curl -X POST "https://www.proofeditor.ai/api/agent/abc123/edit/v2?return=minimal" \
-H "Content-Type: application/json" \
-H "x-share-token: xxx" \
-H "X-Agent-Id: ai:compound-engineering" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg base "$EDIT_BASE" '{by:"ai:compound-engineering",baseToken:$base,operations:[{op:"find_replace_in_doc",find:"old",replace:"new",occurrence:"all"}]}')"Workflow: Create and Share a New Document
# 1. Create
RESPONSE=$(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 token
URL=$(echo "$RESPONSE" | jq -r '.tokenUrl')
SLUG=$(echo "$RESPONSE" | jq -r '.slug')
TOKEN=$(echo "$RESPONSE" | jq -r '.accessToken')
# 3. Bind display name via presence
curl -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:compound-engineering" \
-d '{"name":"Compound Engineering","status":"reading","summary":"Uploaded doc"}'
# 4. Share the URL
echo "$URL"
# 5. Make comment/suggestion 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')
OP_RESP=$(curl -s -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:compound-engineering" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg base "$BASE" '{type:"comment.add",quote:"Content here",by:"ai:compound-engineering",text:"Added a note",baseToken:$base}')")
NEXT_BASE=$(printf '%s' "$OP_RESP" | jq -r '.mutationBase.token // empty')
[ -n "$NEXT_BASE" ] && BASE="$NEXT_BASE"
# For content edits, prefer /edit/v2 over rewrite.apply.
SNAPSHOT=$(curl -s "https://www.proofeditor.ai/api/agent/$SLUG/snapshot" \
-H "x-share-token: $TOKEN")
EDIT_BASE=$(printf '%s' "$SNAPSHOT" | jq -r '.mutationBase.token')
curl -X POST "https://www.proofeditor.ai/api/agent/$SLUG/edit/v2?return=minimal" \
-H "Content-Type: application/json" \
-H "x-share-token: $TOKEN" \
-H "X-Agent-Id: ai:compound-engineering" \
-H "Idempotency-Key: $(uuidgen)" \
-d "$(jq -n --arg base "$EDIT_BASE" '{by:"ai:compound-engineering",baseToken:$base,operations:[{op:"find_replace_in_doc",find:"Content",replace:"Updated content",occurrence:"all"}]}')"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
- 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 - Prefer
find_replace_in_docand block-level/edit/v2edits before consideringrewrite.apply - Don't span table cells in a single replace
- Always include
by: "ai:compound-engineering"on every op andX-Agent-Id: ai:compound-engineeringin headers for consistent attribution - Reuse
baseTokenfrom your most recent/stateor/snapshotread; onSTALE_BASE, re-read and retry once
HITL Review Mode
Human-in-the-loop iteration loop for a markdown document shared via Proof. Invoked either by an upstream skill (ce-brainstorm, ce-ideate, ce-plan) handing off a draft it produced, or directly by the user asking to iterate on an existing markdown file they already have on disk ("share this to proof and iterate", "HITL this doc with me"). Mechanics are identical in both cases: upload the local doc, let the user annotate in Proof's web UI, ingest feedback as in-thread replies and agreed edits, and sync the final doc back to disk.
This mode assumes a local markdown file exists. There is no "from scratch" entry — if the user wants a fresh doc, create one with the normal proof create workflow first, then invoke HITL.
Load this file when HITL review mode is requested — whether by an upstream caller or directly by the user.
---
Invocation Contract
Inputs:
- Source file path (required): absolute or repo-relative path to the local markdown file. When an upstream caller invokes this mode, it passes the path explicitly. When the user invokes directly ("share that doc to proof and let's iterate"), derive the path from conversation context — the file the user just referenced, created, or edited. If ambiguous, ask the user which file.
- Doc title (required): display title for the Proof doc. Upstream callers pass this explicitly; on direct-user invocation, default to the file's H1 heading, falling back to the filename (minus extension) if no H1 exists.
- Recommended next step (optional, caller-specific): short string the caller wants echoed in the final terminal output (e.g., "Recommended next:
/ce-plan"). Not used on direct-user invocation — the terminal report simply summarizes the iteration and asks what's next.
Agent identity is fixed, not a parameter: every API call uses agent ID ai:compound-engineering and display name Compound Engineering. Callers do not override this.
Return shape (used by upstream callers to resume their handoff; also shown to the user in the terminal when invoked directly):
status:proceeded|done_for_now|abortedlocalPath: the source file path (same as input)localSynced:trueif Phase 5 wrote the reviewed doc back tolocalPath;falseif the user declined the sync and local is stale. Only present onproceeded.docUrl: the tokenUrl for the Proof docopenThreadCount: number of unresolved threads still in the docrevision: final doc revision after end-sync (only onproceeded)
---
Phase 1: Upload and Wait
1. Read the local markdown file into memory. Remember this content as uploadedMarkdown — Phase 5 compares against it to detect whether anything changed during the session. 2. POST https://www.proofeditor.ai/share/markdown with {title, markdown} → capture slug, accessToken, tokenUrl 3. POST /api/agent/{slug}/presence with X-Agent-Id: ai:compound-engineering, x-share-token: <token>, body {"name":"Compound Engineering","status":"reading","summary":"Uploaded doc for review"} 4. Display prominently in the terminal:
Doc ready for review: <tokenUrl>5. Ask the user with the platform's blocking question tool: AskUserQuestion in Claude Code (call ToolSearch with select:AskUserQuestion first if its schema isn't loaded), request_user_input in Codex, ask_user in Gemini, ask_user in Pi (requires the pi-ask-user extension). Fall back to presenting options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
Question: "Highlight text in Proof to leave a comment. The agent will read each one, reply in-thread or apply the fix, then sync changes back to your local file. What's next?"
Options:
- I'm done with feedback — read it and apply
- I have no feedback — proceed
If the user is still reviewing, they leave the prompt open — the blocking question waits naturally. A third "still working" option would be a no-op wrapper for that.
On I have no feedback — proceed: skip to Phase 5 (end-sync); return to caller with status: proceeded.
On I'm done with feedback: continue to Phase 2.
---
Phase 2: Ingest Pass
A single pass over the current doc state. Deterministic, idempotent, derivable from marks — no session cache, no sidecar state.
At the start of the pass, update presence to status: "acting" with a short summary like "Reading your feedback" so anyone watching the Proof tab sees the agent is live on their comments. Update to status: "waiting" before the Phase 3 terminal report so the tab signals "ball is in your court" while the terminal asks for the next signal. Same POST /presence call as Phase 1 — just different status/summary.
2.1 Read fresh state
GET /api/agent/{slug}/state?kinds=comment
Headers: x-share-token: <token>Capture:
markdown(current body — includes any user direct edits and accepted suggestions)revisionmarks(object keyed by markId, filtered to comment marks)mutationBase.token— the baseToken required for this round's mutations
2.2 Identify marks that need attention
Filter marks to items where all of the following hold:
kindiscomment(the?kinds=commentread should already guarantee this, but keep the local guard)bystarts withhuman:(authored by a human, not the agent)resolvedisfalse- Either
threadhas no entry authored by anyai:*identity, OR the latest entry inthreadis authored byhuman:*with anattimestamp newer than the latestai:*entry (user responded to a prior agent reply)
Skip everything else. Agent-authored marks, resolved threads, non-comment marks, and threads already replied to with no new human response are done. Do not build needs-reply filters from by alone — Proof's full marks bag can include provenance/authored marks that share a human: prefix but are not review comments.
2.3 Read each mark and decide how to respond
The point of HITL is to give the user a natural way to steer the doc without dragging every decision into the terminal. Most feedback can be auto-applied. Only escalate when the agent genuinely can't make a confident call alone.
Real feedback blends types — "this is wrong, rename to Y" is both objection and directive; "why X? I'd prefer Z" is both question and suggestion. Don't force a clean classification. Read the comment text, the anchored quote, and any prior thread replies, and decide:
Can the agent apply a fix directly with confidence? Imperatives ("rename X to Y", "remove this", "add a section about Z") usually qualify. Apply the edit, reply with a one-line summary of what changed, resolve.
Is this a question with a clear answer? Answer in-thread. Resolve if the answer stands on its own. If answering surfaces a new decision the user should weigh in on, leave open and surface it in the terminal report.
Is this a disagreement? ("this is wrong", "contradicts §2", "this won't work"). Evaluate the claim against current content. If the agent agrees, fix and reply "Agreed — updated to X". If the agent disagrees, reply with the reasoning and leave open. Don't silently apply an objection without evaluating it — the whole point is that the user flagged it because they think the plan is wrong.
Is the intent genuinely unclear? First try: attempt the most reasonable interpretation, apply it, and reply "I read this as X — let me know if I should revert." That's cheaper than a round-trip when stakes are low. Ask for clarification only when the interpretations lead to meaningfully different outcomes. When asking, use the platform's blocking question tool for a quick multiple-choice when the options are discrete, or leave it as an open thread comment when free-form response is more natural. Either way the thread stays open so the next pass picks up the user's reply.
Invariant: every attention-needing mark ends the pass with an agent reply in its thread. Unreplied = "still to do" — the next pass re-classifies it. This is what makes the loop idempotent without a sidecar: mark state is the state. Even when the agent disagrees or can't decide, reply (with reasoning or a question) rather than silently skip.
Batch thread replies and resolves. Build all thread responses during the pass, then write them with a single /ops batch whenever possible. comment.reply accepts resolve: true, so a handled thread should usually be one operation, not reply plus resolve. The batched /ops shape uses one baseToken and one mutation:
{"by":"ai:compound-engineering","baseToken":"<token>","operations":[
{"type":"comment.reply","markId":"<id-1>","text":"Updated the terminology.","resolve":true},
{"type":"comment.reply","markId":"<id-2>","text":"I disagree because X; leaving this open."}
]}Only include existing-thread comment mutations in a batch: comment.reply, comment.resolve, and comment.unresolve. Leave resolve off (or set it false) when the thread remains open for a user decision. This replaces the older pattern of N separate reply/resolve calls or sub-agent parallelism; batching is faster, easier to reason about, and creates one authoritative marks mutation.
2.4 Apply edits
The user is collaborating in the doc, not waiting on approval. Every mutation works with live clients — only whole-doc rewrite.apply is gated. Pick the tool that matches intent:
Default: `/edit/v2` for agent-applied content changes. The comment thread is the review/audit trail: the user asked for the change, the agent applies it, then replies in-thread with what changed. Use block edits for direct fixes, insertions, deletions, and coordinated rewrites so the doc does not accumulate extra suggestion marks for work the user already requested.
Use `suggestion.add` with `status: "accepted"` when a visible track-change mark is itself valuable — for example, the user asked to preserve a reject-to-revert affordance for a specific edit, or the change is judgment-sensitive enough that the visible suggestion trail is clearer than only replying in the comment thread. One call creates the suggestion mark and commits the change.
{"type":"suggestion.add","kind":"replace","quote":"<anchor>","content":"<new>","by":"ai:compound-engineering","status":"accepted","baseToken":"<token>"}Use kind: "insert" | "delete" | "replace" as appropriate; all three support status: "accepted".
Use `/edit/v2` especially when:
- Atomicity is required — multiple coordinated edits must commit together or not at all (e.g., insert new section + update a reference in another block + delete the obsolete paragraph).
/edit/v2takes anoperationsarray that commits atomically; separatesuggestion.addcalls can partially succeed. - Pre-user self-correction — the agent is fixing its own output before the user has looked at the doc (e.g., spotted a mistake mid-ingest-pass). A tracked mark would imply "there was an old version," which is misleading from the user's perspective.
- Pure structural insertion with no quote anchor — adding an entirely new block/section where no existing text serves as an anchor.
suggestion.addrequires aquote;/edit/v2hasinsert_before/insert_afterkeyed on blockref. - Structural list-item or block removal —
suggestion.addwithkind: "delete"only deletes the text inside a list item; the bullet marker (*,-, or numeric1.) stays behind as an orphan line. Use/edit/v2 delete_blockto remove an entire block, orfind_replace_in_blockto splice out the item plus its surrounding whitespace cleanly.
# Get snapshot for block refs + baseToken
curl -s "https://www.proofeditor.ai/api/agent/{slug}/snapshot" -H "x-share-token: <token>"
# Apply
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:compound-engineering" -H "Idempotency-Key: <uuid>" \
-d '{"by":"ai:compound-engineering","baseToken":"<token>","operations":[...]}'Per-op body shape (singular block for replace_block; plural blocks:[{markdown},...] for anything that can add content; the server returns 422 on the wrong shape):
{"op":"replace_block","ref":"b8","block":{"markdown":"new content"}}
{"op":"insert_after","ref":"b3","blocks":[{"markdown":"new block"}]}
{"op":"insert_before","ref":"b3","blocks":[{"markdown":"new block"}]}
{"op":"delete_block","ref":"b6"}
{"op":"find_replace_in_block","ref":"b4","find":"old","replace":"new","occurrence":"first"}
{"op":"find_replace_in_doc","find":"old","replace":"new","occurrence":"all"}
{"op":"replace_range","fromRef":"b2","toRef":"b5","blocks":[{"markdown":"..."}]}Block ref values are opaque request tokens tied to the snapshot/baseToken. Re-fetch /snapshot for fresh refs before another /edit/v2 call if any writes have landed since the last snapshot. Full successful /edit/v2 responses include a fresh mutationBase.token and, unless ?return=minimal was used, a fresh snapshot for chaining.
Bulk mechanical sweep — prefer `find_replace_in_doc` when the rule is literal. For terminology renames, punctuation swaps, or other literal doc-wide replacements, use one /edit/v2 operation:
{"op":"find_replace_in_doc","find":"old term","replace":"new term","occurrence":"all"}Run the same payload through /edit/v2?dryRun=1 first for large sweeps; dry-run returns valid, appliedCount, and per-op results[] without writing. For the real write, use /edit/v2?return=minimal when you only need ok, revision, appliedCount, and the next mutationBase.token. Responses with operationResults include per-block match counts; treat those refs as reporting/display data and re-read /snapshot before follow-up block-ref mutations.
When the edit is semantic rather than literal, batch replace_block, insert_*, delete_block, or replace_range operations in one /edit/v2 call. Use suggestion.add + accepted when edits are distinct and each deserves its own visible reject-to-revert trail.
Use pending `suggestion.add` (no status) when the change is judgment-sensitive enough that the agent wants explicit user approval before commit — rare in HITL, since the point of auto-applied edits is to reduce round-trips. Most judgment-sensitive cases are better handled by leaving the thread open with a clarifying question.
`rewrite.apply` is not needed during a live review. It's blocked by LIVE_CLIENTS_PRESENT anyway.
Mutation requirements (every write, including replies and resolves):
- Top-level field is
typeon single/opswrites; top-leveloperationson/opscomment batches;operations[].opon/edit/v2. Do not mix/opstypeentries with/edit/v2opentries. - Include
baseTokenfrom/state.mutationBase.token(or/snapshot.mutationBase.tokenfor/edit/v2). - Set
by: "ai:compound-engineering"and headerX-Agent-Id: ai:compound-engineering. - Include an
Idempotency-Keyheader. Reuse the same key only for an exact same-body resend; if you rebuild the body with a freshbaseToken, mint a new key. - Successful mutation responses include the next
mutationBase.token; reuse it for the next write instead of re-reading only to get a token. - Reply and resolve together when done:
{"type":"comment.reply","markId":"<id>","text":"...","resolve":true}inside a/opsbatch. Reopen if needed:{"type":"comment.unresolve", ...}.
Retry after any error is verify-first, not retry-first. The Proof API can commit canonically and still return a non-2xx or a 202 with collab.status: "pending"; network timeouts can hit after the server has already written. Retrying without verifying is the most common cause of duplicate marks (same comment twice, same suggestion twice) that then need a manual cleanup pass.
- On
STALE_BASE/BASE_TOKEN_REQUIRED/MISSING_BASE/INVALID_BASE_TOKEN: pre-commit, token-related. Re-read/state, rebuild the request body with a freshbaseToken, and retry once with a newIdempotency-Key. Themutate()helper below auto-retries these. - On
ANCHOR_NOT_FOUND/ANCHOR_AMBIGUOUS: pre-commit, but thequoteno longer matches uniquely. Re-read is not enough; the caller must tighten or regenerate the anchor before retrying. The helper surfaces the error instead of auto-retrying. - On
INVALID_OPERATIONS/INVALID_REQUEST/INVALID_REF/INVALID_BLOCK_MARKDOWN/INVALID_RANGE/INVALID_MARKDOWN/ 422: the payload is wrong. Do not retry — fix the payload and send a new write. - On
COLLAB_SYNC_FAILED/REWRITE_BARRIER_FAILED/PROJECTION_STALE/INTERNAL_ERROR/ 5xx / network error / timeout / 202 with `collab.status: "pending"`: the write may have landed. Re-read/state, diff against the intended change (mark exists? suggestion applied? quote replaced?), and only retry if the server did not actually commit it. If the diff shows the write did land, treat the call as successful even though the response said otherwise.
When the loop breaks. If a mutation keeps failing after a fresh read and a verified-needed retry, or two reads disagree about state, call POST https://www.proofeditor.ai/api/bridge/report_bug with the request ID, slug, and raw response body before falling back. Don't silently skip — that loses the audit trail the user is relying on.
---
Phase 3: Terminal Report
Exception-based. Don't replay what the user can already see in the Proof doc — the full reasoning for each thread lives there. The terminal is for the decisions the user needs to make next.
Every report covers three things, phrased naturally for the current state:
- What got handled (e.g., how many comments resolved, any edits auto-applied)
- What's still open — if any escalations remain, each one gets one line of anchored quote plus one line of the agent's reply or question. Fuller context stays in the Proof thread
- The doc URL — always include it; the user may have closed the tab
Keep the whole report scannable at a glance. Three common shapes fall out of this naturally:
- A clean pass with everything handled collapses to a single line plus the doc URL
- An escalation pass lists the open threads compactly after a one-line summary of what was handled
- A pass with no new feedback just notes that and points to the doc
Phrase them in whatever voice matches the situation rather than matching a template — "handled 4, 1 still needs you" and "all 5 addressed, doc's ready" are both fine.
---
Phase 4: Next-Signal Prompt
Ask the user with the platform's blocking question tool: AskUserQuestion in Claude Code (call ToolSearch with select:AskUserQuestion first if its schema isn't loaded), request_user_input in Codex, ask_user in Gemini, ask_user in Pi (requires the pi-ask-user extension). Fall back to presenting options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
Question: "Proof review pass done. What's next?"
Offer options that cover these intents — use concrete user-facing labels, not agent-internal jargon (no "end-sync", "ingest pass", etc.). Only include the options that fit the current state. Keep labels imperative and third-person (no "I'll" / "I'm" — it is ambiguous in a tool-mediated menu whether the speaker is the user or the agent) and keep the [short label] — [description] shape consistent across every option. A "still working, come back later" option is not offered: the blocking question already waits, so that option would be a no-op wrapper.
- Discuss →
Discuss — walk through the open threads in terminal
Talk through open threads in the terminal; the agent echoes decisions back to Proof threads. Only useful when escalations are open.
- Proceed →
Save — save the reviewed doc back to the local file
Go to Phase 5 end-sync. If escalations are still open, name that in the label (e.g., Save with 3 threads still open) so the user is accepting the tradeoff explicitly instead of via a nested confirm.
- Another pass →
Re-check — look for new comments in Proof
Re-read state and re-ingest. Worth offering even after a clean pass, since the user may have added comments while the report rendered.
- Done for now →
Pause — stop without saving
Stop without syncing; return to caller with status: done_for_now, no end-sync.
The sync confirmation happens in Phase 5 regardless of whether threads are open — this step only asks what the user wants next, not whether to overwrite the local file.
---
Phase 5: End-Sync
Runs when the user selects Proceed. Before prompting anything, check whether the Proof content actually diverged from what was uploaded — if not, there's nothing to sync and no reason to ask.
1. Fetch current state: GET /api/agent/{slug}/state with x-share-token: <token>. Save the full response body to a temp file ($STATE_TMP) so the markdown bytes can later be streamed to disk without passing through $(...) (which would strip trailing newlines). Extract state.revision from that file into $REVISION. Read state.markdown from that file for the comparison in step 2.
2. Compare state.markdown to uploadedMarkdown (captured in Phase 1).
If identical — no content changes happened during the session. Skip the sync prompt entirely. Display:
No changes to sync. Local file is unchanged.
Doc: <tokenUrl>Set presence status: completed, summary "Review complete, no changes". Return to the caller with status: proceeded, localSynced: true (local matches Proof — no write needed, local is not stale), revision: <state.revision>, and the rest of the standard fields.
If different — continue to step 3.
3. Ask with the platform's blocking question tool: AskUserQuestion in Claude Code (call ToolSearch with select:AskUserQuestion first if its schema isn't loaded), request_user_input in Codex, ask_user in Gemini, ask_user in Pi (requires the pi-ask-user extension). Fall back to presenting options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.
Question: "Sync the reviewed doc back to <localPath>? Proof has your review changes; local still has the pre-review copy."
Options:
- Yes, sync now (default, recommended)
- Not yet, I'll pull it later (returns to caller with
localSynced: false)
Why the extra prompt: the user may have started review hours ago and lost track of the local file at stake. A brief confirm makes the file write visible rather than a silent side-effect of clicking Proceed earlier. The caller signals via localSynced so downstream workflows can warn that local is stale.
4. On Yes, sync now, write the fetched markdown to local — see Workflow: Pull a Proof Doc to Local in SKILL.md:
# $STATE_TMP is the temp file holding the /state response from step 1.
TMP="${SOURCE}.proof-sync.$$"
jq -jr '.markdown' "$STATE_TMP" > "$TMP" && mv "$TMP" "$SOURCE"
rm "$STATE_TMP"Stream .markdown bytes directly from the saved state file with jq -jr — do not capture the markdown into a shell variable, since $(...) would strip trailing newlines and corrupt the write. $REVISION (extracted separately in step 1) is safe to keep as a variable; it's an opaque scalar.
On Not yet, skip the write (still clean up $STATE_TMP).
5. Set presence status: completed, summary "Review synced to <localPath>" (or "Review complete, local not updated" if sync was declined) so the Proof UI shows the loop has finished.
6. Display one of:
Synced:
Doc synced to <localPath> (revision <N>).
Doc: <tokenUrl>Declined:
Review complete. Local file kept as-is — pull from Proof when ready.
Doc: <tokenUrl>7. Return to the caller with:
status: proceeded
localPath: <source>
localSynced: true | false
docUrl: <tokenUrl>
openThreadCount: <K>
revision: <N>Do not delete the Proof doc. It remains the durable review record; the caller's workflow may want to link back to it.
---
Recipes
BaseToken-aware mutation
Seed baseToken from the most recent /state or /snapshot read, then update it from each successful mutation response's mutationBase.token. Only re-read on STALE_BASE / BASE_TOKEN_REQUIRED or when you need fresh document/comment/snapshot content. For an ingest pass this means one comment-filtered /state read, one /edit/v2 batch if content changes are needed, and one /ops comment batch for replies/resolutions.
Two retry classes, and they behave differently. The helper below only covers the safe class; the ambiguous class needs a caller-supplied verifier because "did this write land?" depends on what the payload was (look for a markId, a quote replacement, a thread reply, etc.).
SLUG=<slug>
TOKEN=<accessToken>
AGENT_ID=ai:compound-engineering
BASE=<cached from most recent /state or /snapshot read>
mutate() {
local PAYLOAD="$1" # jq template without baseToken
local IDEM_KEY BODY RESP CODE NEXT_BASE
# Fresh key for this request body. If the body changes, including because
# baseToken changes after STALE_BASE, the retry below mints a new key.
IDEM_KEY=$(uuidgen)
BODY=$(jq -n --arg base "$BASE" --argjson payload "$PAYLOAD" '$payload + {baseToken: $base}')
RESP=$(curl -s -X POST "https://www.proofeditor.ai/api/agent/$SLUG/ops" \
-H "Content-Type: application/json" \
-H "x-share-token: $TOKEN" \
-H "X-Agent-Id: $AGENT_ID" \
-H "Idempotency-Key: $IDEM_KEY" \
-d "$BODY")
CODE=$(printf '%s' "$RESP" | jq -r '.code // .error // empty')
# Pre-commit token-related errors — safe to auto-retry with the same
# payload and a fresh baseToken. Anchor errors (ANCHOR_NOT_FOUND,
# ANCHOR_AMBIGUOUS) are also pre-commit but require a tighter quote,
# so they are surfaced instead of auto-retried.
if [ "$CODE" = "STALE_BASE" ] \
|| [ "$CODE" = "BASE_TOKEN_REQUIRED" ] \
|| [ "$CODE" = "MISSING_BASE" ] \
|| [ "$CODE" = "INVALID_BASE_TOKEN" ]; then
BASE=$(curl -s "https://www.proofeditor.ai/api/agent/$SLUG/state" \
-H "x-share-token: $TOKEN" | jq -r '.mutationBase.token')
BODY=$(jq -n --arg base "$BASE" --argjson payload "$PAYLOAD" '$payload + {baseToken: $base}')
IDEM_KEY=$(uuidgen)
RESP=$(curl -s -X POST "https://www.proofeditor.ai/api/agent/$SLUG/ops" \
-H "Content-Type: application/json" \
-H "x-share-token: $TOKEN" \
-H "X-Agent-Id: $AGENT_ID" \
-H "Idempotency-Key: $IDEM_KEY" \
-d "$BODY")
fi
NEXT_BASE=$(printf '%s' "$RESP" | jq -r '.mutationBase.token // empty')
if [ -n "$NEXT_BASE" ]; then
BASE="$NEXT_BASE"
fi
printf '%s' "$RESP"
}The Idempotency-Key is minted for the exact request body being sent. If a retry rebuilds the body with a fresh baseToken, that is a different payload hash, so it needs a new key; reusing the previous key would trigger IDEMPOTENCY_KEY_REUSED. Reuse the same key only for a transport-level resend of the exact same body. Minting outside the function would make unrelated writes share one key, and the server would treat later payloads as invalid key reuse.
Ambiguous failures (anything outside the pre-commit set above — `COLLAB_SYNC_FAILED`, `INTERNAL_ERROR`, 5xx, network timeout, 202 with `collab.status: "pending"`): do not retry from this helper. Re-read /state in the caller, diff the marks/content against the intended change, and only re-issue the write if the diff proves nothing landed. Pattern:
# After an ambiguous failure on comment.add with quote "X" and text "Y":
STATE=$(curl -s "https://www.proofeditor.ai/api/agent/$SLUG/state" \
-H "x-share-token: $TOKEN")
LANDED=$(printf '%s' "$STATE" | jq --arg q "X" --arg t "Y" \
'[.marks[]? | select(.by == "ai:compound-engineering" and .quote == $q and (.thread[0].text // .text) == $t)] | length')
if [ "$LANDED" -gt 0 ]; then
echo "Already applied — skipping retry."
else
# Safe to retry with a fresh BASE.
...
fiMatch on a payload-identifying field (quote + text for comment.add; quote + content for suggestion.add; markId + text for comment.reply; block ordinal + markdown for /edit/v2). When no such invariant is available, prefer leaving the write undone and surfacing it in the terminal report over risking a duplicate.
jq gotcha when inspecting responses
When extracting fields from API responses with jq's // alternative operator, parenthesize inside object constructors — jq parses {markId: .markId // .result.markId} as a syntax error. Use {markId: (.markId // .result.markId)}, or pull the value outside the object: jq -r '.markId // .result.markId'.
Identity
All ops must include:
by: "ai:compound-engineering"in the request bodyX-Agent-Id: ai:compound-engineeringin headers (required for presence; recommended for ops for consistent attribution)
Display name Compound Engineering is bound via POST /presence with {"name":"Compound Engineering", ...}. Set this once after upload; it carries across subsequent ops.