Swasti · mForm V2→V3

Validation source — hypothesis resolved

Companion to 03-mform-json-analysis.md (gap A14c) and 04-mform-to-frappe-skill.md (§10). Resolves where mForm validations actually live.

Conclusion (one line)

Validations come from per-question keys (pattern, min, max, minRange, maxRange, restrictions, editable, isToBeEncrypted) plus input_type-implicit rules. The validation[] array with null condition is unused / placeholder. A14c is resolved — no mForm-team escalation needed.

Evidence

Top-level python3 + collections.Counter over every question in form 1000 (Member Profile, 56 questions) and form 1003 (Scheme Followup, 37 questions). Question-level keys we missed in 03-mform-json-analysis.md:

Key1000 (n=56)1003 (n=37)What it is
order5637universal join key (already documented)
label5637display label
shortKey5637short identifier — likely the basis for Frappe fieldname derivation
viewSequence5637render order
input_type5637mForm widget code → drives Frappe fieldtype
validation5637array; condition always nullplaceholder, ignore
restrictions5637container for restriction rules — present on every question
child5637child-question reference (skip logic / conditional show)
parent5637parent-question reference
editable5637read/write flag → Frappe read_only
weightage5637scoring weight
isToBeEncrypted5428encrypt-at-rest flag (mostly true)
pattern217regex pattern (where applicable) → Frappe field-level regex / JS validation
min / max217length or numeric bound
minRange / maxRange115range bound (numeric questions)
valueHolder62placeholder text

Bolded keys are validation-bearing. They fully explain the rules.

What this means for the import pipeline

For each question, the mapper must read:

  1. input_type → Frappe fieldtype (Data / Int / Float / Select / Link / Date / Table …) — implicit type validation comes free.
  2. editable === falseread_only: 1.
  3. pattern → field-level regex / JS client-script validator.
  4. min/max → if the field is text, treat as length bounds; if numeric, treat as min/max value. Disambiguate from input_type.
  5. minRange/maxRange → numeric range — likely on slider/range input types specifically.
  6. restrictions → open the array shape on at least one form before mapping (we know it’s there, but its keys weren’t sampled in the Counter pass; treat as TODO for the skill author to enumerate).
  7. isToBeEncrypted === true → set the Frappe field’s encryption flag (or post-process via a hook — depends on Frappe version).

validation[].condition === null everywhere → do not consume.

Skill architecture confirms this layered model

The skill’s own documentation (raw/mform-to-frappe-skill/mform_to_frappe/PLAN-corrections.md:115) names a reference file validation-layer-matrix.md, and :258 mentions _validate_conditional_mandatory() — i.e., the skill is already designed for the Frappe validation layers (DocType field flags + JS client script + Python _validate_*() server hooks + child-table rules). That matrix is the canonical lookup; my Counter pass above tells you which mForm keys feed each layer.

Recipes that already encode validation patterns:

  • recipes/did-range-filter.mdInt + Select with min/max range
  • recipes/geography-cascade.md — cascading Link fields (validation = “must exist in master at level N”)
  • recipes/fetch-from-read-only.mdLink + read_only: 1 (editable: false mapping)
  • recipes/form-with-loops.mdTable for input_type 20/21
  • recipes/conditional-visibility.mddepends_on for skip logic
  • recipes/scoring-computation.md — uses weightage

Action item update

  • A14c — RESOLVED locally. Strike from “things to ask mForm team”. Replace with a one-line note in §10 of 01-kickoff-brief.md: validations come from per-question keys + input_type; the skill’s validation-layer-matrix.md is the canonical reference.
  • A14a (translations) and A14b (master data values) remain open and DO need mForm-team answers.
  • New micro-task for the dev: when picking up A16, dump the actual restrictions array shape from one form to confirm its sub-keys. The Counter pass shows it’s present but didn’t enumerate its contents.

Surfaced anti-patterns (skill-author warnings)

The skill repo has TODO/FIXME-style notes worth surfacing to whoever picks up A16:

  • mform_to_frappe/PLAN-corrections.md:986 — “Map every question → Frappe fieldtype” is the third step in their plan, not the first. Step 1 is the (formId, order) → fieldname registry (matches our Job-1 finding).
  • mform_to_frappe/PLAN-corrections.md:534 — references a “verified 22-widget list” — there’s a known finite widget set; rare input_type codes 13/29/30 (from 03-mform-json-analysis.md) need a sample before mapping or pipeline must hard-fail.

Last updated 2026-05-04