feat(forms): extend the persisted batch primitive to Forms (forms.batchUpdate is the only mutation endpoint, exposed one request at a time)
The gap
The Forms API has exactly one mutation endpoint: forms.batchUpdate, taking an ordered list of CreateItem / UpdateItem / MoveItem / DeleteItem / UpdateFormInfo / UpdateSettings requests.
gog forms exposes that endpoint one request at a time: add-question, delete-question, move-question, update, publish. Building a form is therefore one API call per question, and gog batch cannot help because it is Docs-only. gog batch begin accepts --doc and nothing else, and no gog forms command carries --batch.
The cost
Documented Forms quota is 150 write requests per minute per user per project (https://developers.google.com/workspace/forms/api/limits), so this is less likely to 429 than Docs, Sheets or Slides at 60. The argument here is ordering and atomicity rather than quota.
A generated form is the normal case for this command: a survey or an intake form built from a source file, thirty to sixty questions. Today that is thirty to sixty sequential calls, each of which can fail on its own, leaving a half-built form with no clean way back. A caller who wants a reproducible form has to write its own idempotence, because re-running the script duplicates every question rather than replacing it.
Index-based positioning makes the per-call shape worse than it looks. add-question inserts at an index, and every subsequent insert shifts the indices the caller computed. A script that builds thirty questions has to track index drift across thirty separate calls, where a single forms.batchUpdate applies the whole ordered list against one starting state.
What a caller has to do today
Compute indices defensively, re-read the form between calls, and accept that a mid-run failure leaves a partial form. Or drop to a Google API client library and post one batchUpdate, which is what the API was designed for.
Proposed surface
Same shape as the shipped Docs batch:
gog batch begin --form=<id> [--name=<label>]
gog forms add-question --batch=<id> ...
gog forms delete-question --batch=<id> ...
gog forms move-question --batch=<id> ...
gog forms update --batch=<id> ...
gog batch end <id>--dry-run, --continue-on-error and the revision lock behave as they do for Docs. forms.batchUpdate carries includeFormInResponse and a writeControl with requiredRevisionId, so the revision lock maps directly.
get, raw, responses and watch are reads or a different endpoint and stay out.
A batched path also opens a forms equivalent of building a whole form from a source file in one submission, which is the workload most callers actually want and cannot express today.
Source: openclaw/gogcli