Skip to content

docs: shell-scripting gotchas for bulk ops against the agent API #41

@rossgroomio

Description

@rossgroomio

Posting here as the public-facing SDK repo — happy to re-route if the canonical SKILL.md source lives elsewhere.

While scripting a bulk comment/reply loop (14 comment.reply + comment.resolve ops) against a hosted Proof doc via the agent API (/api/agent/<slug>/ops), I hit four shell-level gotchas that aren't covered by the SKILL.md served at https://www.proofeditor.ai/proof.SKILL.md. Each one fails silently or with an obscure error rather than a clean API error.

Worth a short "Shell Patterns" section in the skill doc, or a linked appendix — authors of agent loops keep rediscovering these.

1. .marks is an object, not an array

The /state and /snapshot response's marks field is keyed by markId, not indexed. jq '.marks[0]' fails with Cannot index object with number. MarkIds look like m1776098130482_1 (comments) or authored:human:<name>:<range> (text anchors).

# Wrong
jq '.marks[0]' state.json

# Right
jq '.marks | to_entries[]' state.json

2. for row in $(jq -c '.[]' file) word-splits JSON

Bash splits $(...) on whitespace, including spaces inside JSON string values. Any op payload with a space in text or content fragments across loop iterations. Use a while-read pipe:

jq -c '.[]' ops.json | while IFS= read -r row; do
  curl ... -d "$row"
done

3. baseToken rotates per mutation — refetch inside loops

The current SKILL.md documents baseToken and the 409 BASE_TOKEN_REQUIRED retry path well. The missing piece: for a loop of N ops, a single upfront fetch of mutationBase.token from /snapshot goes stale after op 1 and ops 2..N all fail with Document changed since baseToken until you refetch. Worth calling out explicitly as "refetch per iteration for bulk loops" alongside the existing retry-on-409 guidance.

4. jq parse failure on the response ≠ API failure

Op responses occasionally contain control characters (U+0000–U+001F) that break jq parsing with Invalid string: control characters ... must be escaped. The server-side op may have landed fine. Recommended pattern: fall back on parse failure and verify against /state rather than trusting the parse exit code.

RESULT=$(curl -sS -X POST ".../ops" --data @op.json 2>/dev/null)
STATUS=$(echo "$RESULT" | jq -r 'if .ok == true then "ok" elif .error then .error else "unknown" end' 2>/dev/null || echo "parse-fail-check-state")
# Then re-read /state to confirm the op landed

Happy to open a PR

Can you point me at the canonical source for the SKILL.md served at proofeditor.ai/proof.SKILL.md so I can put up a PR with these as a new section? I couldn't find an obvious match in this repo's docs/proof.SKILL.md — that file describes a /documents/<slug>/edit/v2 + baseRevision API surface rather than the agent API my scripting session used, which suggests the hosted SKILL.md comes from somewhere else.

Reproduced on hosted Proof, 2026-04-13.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions