Example corpus
Five forms that are meant to be run, not just read. Every one of them is
validated on every CI run — by validateFormDefinition (packages/core/test/corpus.test.ts)
and, separately, against the published JSON Schema in Ajv strict mode
(tools/validate-schema.mjs). A form that stops validating fails the build.
rasd validate docs/examples/*.form.json
The forms
| File | What it is | What it is here to cover |
|---|---|---|
pdm-food-distribution.form.json | pdm-gfd-2026 — post-distribution monitoring for a general food distribution, ar/en | The reference tool. matrix, rank, rating, barcode, consent, a custom x: type, field-level encryption, a household roster |
facility-monitoring-visit.form.json | A health-facility monitoring visit, ar/en | geotrace, choice filters, three datasets, a deep relevance tree, signature |
wash-water-point-survey.form.json | Quarterly water point inspection, en/fr | geoshape, datetime, audio, a custom validator, navigation: "scroll", a non-Arabic second locale |
nutrition-screening-referral.form.json | MUAC screening and referral at a mobile clinic, ar/en | video, file, Arabic as the default locale, numbering: "native", calendar: "islamic-umalqura", encryption.mode: "submission", requires.features |
pdm-300.form.json | The NFR-002 benchmark: 302 questions, 14 pages, 175 kB | Performance only. Generated — see below |
Between them they exercise every built-in element type, every validator type and every settings option the spec defines. That is not decoration: it is why adding a type without touching this directory shows up as a gap rather than as nothing.
theme-agency-blue.json is the example theme, checked
by tools/validate-theme.mjs for WCAG 2.2 AA contrast.
The benchmark form
pdm-300.form.json is generated by tools/build-pdm-300.mjs
and checked in. Do not edit it by hand — CI regenerates it and fails on any
difference.
It is deliberately hard rather than merely large: every module is gated on a select, forty calculations form a single dependency chain, aggregates fan in across pages, and one choice list has 400 options. Three hundred independent text inputs would measure almost nothing.
node tools/build-pdm-300.mjs # regenerate
node tools/build-pdm-300.mjs --check # what CI runs
The negative corpus
invalid/ holds one fixture per error code
validateFormDefinition can raise — fifty of them, each named for its code and
each carrying exactly one violation.
rasd validate docs/examples/invalid/E_CALC_CYCLE.form.json
# E_CALC_CYCLE /pages/0/elements/1 Calculation cycle: a → b → a
Some need an option, given in invalid/manifest.json:
rasd validate docs/examples/invalid/E_TOO_MANY_PAGES.form.json --limit pages=1
rasd validate docs/examples/invalid/E_VERSION_REUSED.form.json \
--previous docs/examples/invalid/E_VERSION_REUSED.previous.json
The fixtures that test a limit tighten it rather than shipping a form that
genuinely exceeds the default — a 2001-element document would add megabytes to
the repository to exercise one comparison. The default values themselves are
pinned against docs/04 §16 in corpus.test.ts.
These files are generated by tools/build-invalid-corpus.mjs
from a table of one-line mutations, so the only thing that differs between two
of them is the violation each one carries.
The test that matters runs in the other direction: every error code found in
validate.ts must have a fixture, so a new rule without a fixture fails CI. That
is the half that keeps this from rotting — a corpus only ever proves things
about the rules someone remembered to write a file for, and the rules nobody
remembered are exactly the ones that quietly stop working. When it was first
switched on it found fourteen limit guards that no test had ever fired, and a
class of malformed expression that rasd validate had been calling clean.