Schema
Form YAML contract for gform. This is the page agents and humans should learn first when editing answers. Command UX (gform edit, --ask, editors) lives in commands/edit — keep that page for how to open/save, not for field tables.
- Docs: thegform.pages.dev/schema
- Zod source:
packages/core/src/config/schema.ts
gform verify, edit --ask Save, and run all enforce this shape. Unknown keys are rejected.
Every write from init / edit --ask save / restore stamps a short header that points here.
Top-level file
| Field | Required | Notes |
|---|---|---|
name | yes | Library id; must not be a reserved run subcommand (list, ls, view, v, last) |
url | yes | Absolute Google Forms viewform URL |
description | no | Free-text summary for humans / agents |
tags | no | String labels (listing / filter later) |
schemaVersion | no | Optional integer when you break your own conventions |
options | no | Defaults applied when omitted |
steps | yes | At least one step |
# gform form — https://thegform.pages.dev/schema
# agents — https://thegform.pages.dev/agents
# …
name: my-form
url: https://docs.google.com/forms/d/e/…/viewform
options:
headed: false
variantMode: rotate
steps:
- action: text
question: Name
value: Ada
- action: submitoptions (form config — not run --policy)
Run presets (safe / demo / load / watch) are CLI --policy only. Form YAML owns variants, unique rules, and starters.
| Key | Default | Role |
|---|---|---|
headed | false | Starter for headed launch. Applies only when CLI --headed / headed --policy did not set it. Run intro shows source. |
repeat | (omit) | Optional starter for -r. CLI -r / policy win when set. |
variantMode | rotate | How step pools pick across repeats: rotate · random · zip (equal-length persona rows) |
unique | (omit) | { scope: history|batch, fields: [question titles…] } — skip when those resolved answers already succeeded (independent of --once / -sd). |
slowMo | 100 | Playwright slow-mo ms |
timeoutMs | 30000 | Navigation / action timeout |
locale | en-US | Forms UI + browser locale (hl= + Playwright) |
labels | engine defaults | Optional button label overrides: next / back / submit string arrays |
Starter precedence (highest wins): explicit CLI → named --policy → form YAML → engine default. Never silent YAML overrides — see commands/run.
unique detail
options:
unique:
scope: history # or batch
fields: [Email, Name]scope | Blocks when |
|---|---|
history | Same field values already succeeded in past runs or this batch |
batch | Same field values collide inside this --repeat wave only |
Always enforced when set — separate from whole-payload --skip-duplicates / --once.
steps (discriminated by action)
Every step may include optional note (docs only — ignored by fill).
| Action | Fields | Notes |
|---|---|---|
text | question, value, optional pool | Short / paragraph / date-time-as-text |
radio | question, value, optional pool | Single choice, linear scale, star rating |
select | question, value, optional pool | Dropdown |
checkbox | question, values[], optional pools[] | Multi-select; pools = one pool per value slot |
next / back | — | Multi-section navigation |
submit | — | Final submit |
file | question, optional source (local|gdrive|remote), pool/urls, accept, maxBytes, message | Auto-upload when source set; else headed assist. Deep dive: file-uploads. |
pause | optional message | Soft-wall wait for the operator (TTY) |
wait | ms (default 1000) | Sleep between steps |
File uploads
Use nested source: only — bare path: / url: on the file step are not accepted.
- action: file
question: Resume
source:
type: local
path: ./files/resume.pdf
accept: [pdf] # categories (presentation) or exts (pptx / .pptx)
maxBytes: 10485760- action: file
question: Signed PDF
source:
type: gdrive
# Support both — id wins if both set; link alone is parsed
id: 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
link: https://drive.google.com/file/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/view
accept: [pdf]- action: file
question: Photo
source:
type: remote
url: https://example.com/avatar.png
accept: [image]Omit source for assist-only (operator uploads in the headed browser — headless fails with assist-headed-required). Drive / My Drive picks are detected via the Forms Selected files list (data-id chip). When possible, gform fetches bytes with the signed-in session into artifacts/<id>/content/; otherwise metadata-only. Configured sources always copy into content/ on success. Status on gform run view: uploaded / assisted / skipped.
Full behavior map (decide logs, pools, chip detect, headed cheat sheet): file-uploads.
Pools
- action: radio
question: "Age range?"
value: "18-25"
pool: ["18-25", "26-35", "36-45"]- action: checkbox
question: "Skills"
values: ["Python"]
pools:
- ["Python", "Go", "Rust"]pool— single-answer steps (text/radio/select/filepaths)urls—fileremote URL pool (same variant rules)pools— checkbox only; length should match how you vary each slot across repeats- Without pools,
--repeat+--skip-duplicatesmostly skips (same fingerprint)
See repeat.
Variant modes
| Mode | Behavior |
|---|---|
rotate | Deterministic: run index i → pool[i % length] |
random | Pick randomly each run |
zip | Shared index: every pooled field takes slot i (name[i] with email[i]). Needs ≥1 pool and equal lengths — mismatch → hard error on verify / load / run. |
options:
variantMode: zip
steps:
- action: text
question: Name
value: Ada
pool: [Ada, Grace, Alan]
- action: text
question: Email
value: ada@example.com
pool: [ada@example.com, grace@example.com, alan@example.com]-r 3 → three personas. Preview rows with gform view. Agents must ask before assuming zip vs rotate/random. Optional later sugar: top-level rows: / personas: (expands to the same pools — not shipped).
How validation runs
| Surface | When |
|---|---|
gform verify <form> | Explicit Zod check |
gform verify <form> --check | Live inspect → safe YAML sync (accept + new stubs); -A / -y with --check only |
edit --ask → Save / Preview | Zod before write; Preview shows validity |
gform run | Loads via the same schema |
Fail closed on unknown keys and bad types — fix YAML, do not invent fields. variantMode: zip with unequal pool lengths fails verify / run (Zod still loads so gform view can diagnose).
Related
- agents — flags, auth, artifacts, JSON mastery
- concepts — mental model
- file-uploads — file source / assist / Drive map
- inspect-verify — live inspect + verify --check
- commands/edit — open YAML /
--ask - commands/verify — CLI verify
- repeat — pools across repeats
- commands/run — provenance + policies
- commands/init — scaffold from a live form