# Default Values {#default-values}



## Overview {#overview}


Default values allow survey questions to be pre-populated with initial answers when a form starts. The system supports four expression types: question references, system variables, date/time keywords, and static values.

Default values are resolved in a deterministic phase before any calculation expressions are evaluated. They are **init-time only** — once resolved, they become regular editable answers and are never recomputed.


## Expression Syntax {#expression-syntax}


Default values use a lightweight expression language with three token types:


### Question References (`#varname`) {#question-references-varname}


References another question's answer by name:

| Expression | Result | Explanation |
|---|---|---|
| `#country#res_num#psu_resnum` | `USA001NY` | Concat country + res_num + psu_resnum values (no separator) |
| `#fname #lname` | `John Doe` | Concat fname + space + lname values |
| `hello #country world` | `hello USA world` | Literal text mixed with references |
| `#username#country` | `JohnUSA` | System variable and question reference together |

When a referenced question has no answer (empty or not yet filled), it resolves to an empty string.


### System Variables (`#username`, `#email`, `#deviceid`) {#system-variables-username-email-deviceid}


Pre-defined variables resolved from the user's session at form start:

| Variable | Resolves To | If Unavailable |
|---|---|---|
| `#username` | Current user's display name | Empty string |
| `#email` | Current user's email | Empty string |
| `#deviceid` | Device identifier | Empty string |

System variables are case-insensitive and never block form start — they silently resolve to empty when unavailable (offline mode, missing permissions, etc.).


### Date/Time Keywords {#datetime-keywords}


| Keyword | Format | Example |
|---|---|---|
| `today` | yyyy-MM-dd | `2026-06-12` |
| `now` | ISO 8601 datetime | `2026-06-12T14:30:00.000Z` |

`today` and `now` are whole-string keywords only. They cannot be combined with other text.


### Static Values {#static-values}


Any expression that does not match the above patterns is treated as a static literal:

| Expression | Result |
|---|---|
| `42` | `42` |
| `Pre-filled text` | `Pre-filled text` |
| (empty) | No default is set |


### Formal Grammar {#formal-grammar}


```
expression   := TODAY | NOW | concat | static
TODAY        := "today"  (case-insensitive, whole string)
NOW          := "now"    (case-insensitive, whole string)
concat       := part+
part         := HASH_REF | LITERAL
HASH_REF     := "#" IDENTIFIER
IDENTIFIER   := [a-zA-Z_][a-zA-Z0-9_]*
LITERAL      := any character sequence not part of a HASH_REF
static       := any string not matching above
```

All three runtime implementations (Python, TypeScript, Dart) use this identical grammar — same input always produces the same AST, which always resolves to the same output.


## Resolution Phases {#resolution-phases}


Default values are resolved in a strict sequence during form initialization:

```
Phase 1: Load schema + prefill data
Phase 2: Resolve date/static defaults (eager — no dependencies)
Phase 3: Sort remaining defaults by order_index ASC
Phase 4: Resolve concat/system defaults in order_index order
         (each resolution sees answers from all earlier defaults)
Phase 5: Evaluate calculation expressions (existing logic)
Phase 6: Render UI
```

The `order_index` field is set from the XLSForm row number during import and preserved through round-trips. If a reference points to a question with a higher `order_index` (appears later in the form), it resolves to an empty string at form start.


## Design-Time Validation {#design-time-validation}


At schema save time, default value expressions are validated against all question names in the survey:

| Condition | Behavior |
|---|---|
| `#ref` matches a question name | Pass |
| `#ref` matches a known system variable | Pass |
| `#ref` is unknown (not a question, not a system variable) | **Blocks save** — validation error |
| `#today` / `#now` keyword | Pass |
| Static value (plain text, numbers) | Pass |
| Empty / no default | Pass (no change) |

Validation runs in the `validate_survey_schema()` pipeline alongside schema, choice, logic, and repeat validators. A failed default validation prevents schema publication.


## Additional Values {#additional-values}


The `additional_values` column (separate from `default`) specifies extra accepted choices for `select_one` and `select_multiple` questions. These are values accepted beyond the defined choice list — commonly used for numerical "other" codes like `98` (Don't know) or `9997` (Refused).

| Column | Example | Behavior |
|---|---|---|
| `additional_values` | `98, 9997, 9998` | Adds these as accepted choices |
| `additional_values` | (empty) | No extra values |

Additional values are deduplicated against existing choices at render time. If a value already exists in the choice list, the duplicate is silently skipped.


## Runtime Context {#runtime-context}


The resolution context is constructed at form start and contains:

| Field | Source | Guarantee |
|---|---|---|
| `answers` | Current form data (prefill + resolved defaults) | All values are strings |
| `username` | Auth provider | Never null — `""` if unavailable |
| `email` | Auth provider | Never null — `""` if unavailable |
| `deviceId` | Device ID provider | Never null — `""` if unavailable |


## API {#api}



### Expression Validation Endpoint {#expression-validation-endpoint}


```
POST /api/v1/surveys/expressions/validate
```

```json
{
  "expression": "#country#res_num",
  "expression_type": "default",
  "available_fields": ["country", "res_num", "psu_resnum"]
}
```

Response:

```json
{
  "is_valid": true,
  "expression": "#country#res_num",
  "expression_type": "default",
  "errors": []
}
```

The `expression_type` field accepts `"default"` in addition to `"relevant"`, `"constraint"`, and `"calculation"`.


### XLSForm Columns {#xlsform-columns}


| Column | Type | Description |
|---|---|---|
| `default` | Text | Default value expression |
| `additional_values` | Text | Comma-separated extra accepted choices |
| `order_index` | Integer | Schema position (set automatically) |

The `default` column is part of the standard XLSForm specification. The `additional_values` column is an MQUAD extension. Extra columns not in the standard set are automatically preserved through import/export round-trips.


## Cross-Platform Parity {#cross-platform-parity}


The three runtime implementations are:

| Platform | File | Language |
|---|---|---|
| Backend validation | `survey-service/.../default_value_parser.py` | Python |
| Web form engine | `frontend/.../calculation-engine.ts` | TypeScript |
| Mobile field app | `mobile/.../default_value_resolver.dart` | Dart |

All three follow the same specification. The `validateDefaultExpression()` / `validate()` function is shared between the validation pipeline and the frontend designer UI, ensuring consistent behavior at design time and runtime.
