Qube Script reference
Qube Script (.qube, also called QSS) describes a questionnaire as readable
source. This reference covers implemented constructs. Start with
your first script, then use Workbench or
MCP to check and run the complete instrument.
The full authoring guide is an exact copy of the QUBE-authored canonical guide. It is a text download, not a NIPO manual.
Structure and text
A source starts with one survey header. Questions have stable names, typed answer contracts and Markdown text. An outcome ends the interview.
@survey feedback @language en @mode cawi
-- Author comments do not appear in the interview.
@question helpful boolean
Was the information helpful?
@question comment text @multiline @optional
What could we improve?
@outcome complete
Thank you.Indentation helps reading but does not define blocks. Directives begin with
@; -- starts an author comment. Write labels and prompts as ordinary
Markdown. Quotation marks in an option label are visible text.
Question and option identities belong to the data contract. Keep them stable when changing wording. A new hosted upload creates a version; it does not change an already started attempt.
Answer contracts
| Contract | Meaning |
|---|---|
display | Information with a Continue action; no answer |
boolean | True or false |
integer | Whole number |
decimal | Decimal number |
text | Free text; @multiline requests a larger input |
date, time, datetime, duration | Typed temporal value |
single | One option |
multi | Multiple options |
ranking | Ordered selected options |
allocation | Integer or decimal amounts distributed across rows |
struct <name> | Named fields in one answer |
Questions and struct fields are required by default. @optional allows an
answer to remain absent; @required states the default explicitly. A display
question has no required/optional answer modifier.
Unanswered, unasked and answered-empty are different states. Missing answers do not become zero, false or an empty string. Expressions over answers carry absence; an absent condition is not true.
Options and Other answers
Use inline options for a one-off question. Declare a named domain when its options are shared by questions, ordering or row/column definitions.
@question devices multi
Which devices do you use?
@options
1: Phone
2: Computer
98: Other @specify
99: None of these @exclusive| Modifier | Meaning |
|---|---|
@specify | Attached text for the selected option |
@exclusive | Must be the only selected option; mixed whole-cell answers are rejected |
@noread | An interviewer should not read the option aloud; it remains selectable |
@group id: Heading | Group heading within an option domain |
Option identifiers may be numeric or text. A quoted text identifier such as
"01" preserves its leading zero. Do not use numeric conversion for identifiers.
Applicability and routing
Use @if on a question or field for conditional applicability. Use a named
@route for branching: the first true rule wins. @goto is unconditional.
Targets can name sections, questions or declared endings.
@question consent boolean
Do you agree to participate?
@route consent_check
if answers.consent != true -> screenout
else -> details
@section details
About your experience
@question visits integer
How many times have you visited?
@question reason text @optional @if answers.visits > 1
What brought you back?
@goto complete
@outcome screenout
Thank you for your time.
@outcome complete
Finished.Use a route rather than a conditional @goto. There is no questionnaire-level
@if/@end_if wrapper.
Pages, grids and fields
@page name groups several questions on one screen; @end_page closes it.
The runtime commits the page as a unit. A question on that page cannot depend
on another question on the same page for its applicability.
@rows domain and @columns domain attach axes to a question. For a rating
grid, use single, bind the item domain with @rows, and bind the rating
domain with @options. For allocation, add @rows, @values integer or
@values decimal, and @total 100 (or another required total).
@struct name contains @field field_name type declarations. Ask it with
@question contact struct name. Field contracts support @range, @min,
@max, @length and @pattern. These are typed constraints, not free-form
JavaScript or SQL.
Repeats and ordering
@foreach item in domain … @end_foreach repeats a body for option-shaped
members. A multiselect answer can be its source. Loop answers retain the
member identity and nested repeat path.
@options tools
1: Calendar
2: Messaging
@question used multi
Which tools do you use?
@options tools
@foreach tool in answers.used
@question rating integer
How would you rate {tool.label} from 1 to 5?
@validate rating_range answers.rating >= 1 and answers.rating <= 5
Enter a rating from 1 to 5.
@end_foreachWithin the body, tool.id, tool.label and tool.position identify the
current member. A route or jump out of a loop abandons its remaining members.
The repeat syntax is @foreach, not @repeat.
Question presentation can use @order order(domain).shuffle(). The ordering
API also provides reverse(), reverse_if(condition), first(...),
last(...), fixed(option, position), shuffle(group), shuffle_groups()
and stop_at(...). Keep pinned options outside groups. Ordering is seeded
per case; option identities stay stable when presentation order changes.
Validation
@validate name condition declares a check, followed by the respondent-facing
message. Error is the default severity and blocks progress. Warning/confirm
checks require confirmation. @focus question_name identifies the input.
@question age integer
How old are you?
@validate age_range answers.age >= 18 and answers.age <= 120 @severity error @focus age
Enter an age from 18 to 120.
@validate age_unusual answers.age < 100 @severity confirm @focus age
Please confirm this age.Constraints also include numeric bounds, text length/pattern and selection limits. Check the whole source and run invalid-answer cases; parser acceptance alone does not show that your routing or measurements are correct.
Calculations and text piping
{expression} inserts checked values in Markdown. Read answers with
answers.question_name. Use arithmetic, comparisons, and, or and not
in expressions.
value NAME = expression declares a package constant. It cannot depend on
answers. A function computes from current answers; a function expecting an
ordinary value propagates a missing answer without running its body.
value MONTHS = 12
@question monthly_cost decimal
What is the monthly cost?
function annual_cost() returns answer(decimal) = answers.monthly_cost * MONTHS
@question estimate display
The annual estimate is {annual_cost()}.Function bodies use let, assignments, if, for, return and end.
These statements belong inside functions. A bare assignment at questionnaire
level is prose and can be shown to the respondent.
Back, services and endings
Back is a runtime navigation action, not an authored @back directive.
It preserves history; an unchanged continuation reuses it, while changed
answers create a new branch. Derived calculations and applicability follow
the current answers. Back does not undo an external action already performed.
service.call("name", payload) and service.evaluate("name", payload) use
the recorded environment boundary. An unresolved call suspends until the host
supplies a typed result. The host owns service configuration, authorization,
credentials and budgets. Repeated reads of the same call and scope reuse the
saved observation. See Services for setup and beta limits.
Language support does not mean a service is configured or available offline.
environment.allocate("campaign-quota", "binding_name") suspends for a
recorded boolean quota admission result. The host verifies the preceding
answers and checks the configured shared quota cells; the script routes on the
result. Admission and final completed membership are different facts: changed
final answers can take a cell over target. See Fieldwork for
candidate setup, target management and deployed availability.
service.call("quota-score", "binding_name") uses the same service primitive
for a configured advisory metric in the local candidate. It returns a recorded
struct with integer score (0..1000), target_revision and policy_revision.
Replay reuses the original observation. A score does not grant admission; use
the allocation call separately. See Fieldwork for the bounded
expression language, immutable binding setup and hosted limitations.
@outcome name declares a terminal outcome. @suspension name declares a
resumable stopping point in the native runtime. A host must distinguish these
lifecycle states; no current page does not, by itself, mean successful completion.
The Run’s outcome/retention policy decides what a named outcome means for
hosted collection. A script cannot authorize acceptance or retries.
launch.param("literal_name") reads optional untrusted launch text. The host
freezes consumed values at Start. A missing value is none; a present blank
is empty text. It is data, not account, participant or access authority.
Scope and checks
Use the fictional studies to inspect complete instruments and
independent behavioral requirements. Use qss.check through MCP and run the
instrument before uploading. A complete source has one survey header and an
explicit ending; broken buffers may still produce editor diagnostics.
Do not assume all planned standard-library methods work: saved-order
reuse, domain.where and named frame mapping/filtering are not generally
qualified authoring recipes. Hosted quota availability, cross-device recovery,
collection volume and confidential assessment marking have separate product
limits. Read Fieldwork before collecting real data.