Skip to content
approved Visibility internal Owner _ Approver _ Created _ Updated _

Life Domain Quiz

Purpose

Create, review, update, and validate Uvilo 360° Life Domain Quizzes — the mixed-methods assessments used for each Domain of Life Balance.

When to use

  • Creating a new quiz for a life domain
  • Reviewing/auditing existing quizzes for compliance
  • Updating quiz content (questions, scoring, sections)
  • Validating quiz JSON against conventions and schema

Key files

FilePurpose
{PRODUCT}/Domain_Quiz/LifeDomainQuiz_Spec.mdCanonical spec: full rules, conventions, and acceptance criteria for quiz authoring
{PRODUCT}/Domain_Quiz/Scripts/uvilo_quiz_qc.tsCanonical QC checker (TypeScript CLI)
{PRODUCT}/Domain_Quiz/Schemas/UploadQuizSchema.tsCanonical Zod schema for quiz validation

Quiz file locations

  • Quizzes: {PRODUCT}/Domain_Quiz/Output/
  • Domain definitions: {PRODUCT}/Life Domains/Output/LifeDomainData.json
  • Taxonomy: {PRODUCT}/Taxonomy/Output/Taxonomy.csv

Quiz JSON structure

{
  "slug": "uvilo360_NN_domain_VVV",    // globally unique
  "group": "uvilo360",
  "handle": "NN_domain",               // e.g. "01_body"
  "version": 3,
  "date": "YYYY-MM-DD",
  "name": "Domain (Subtitle)",
  "description": "...",
  "instructions": "...",
  "analysisBotHandle": "analyze-life-domain-quiz",  // required; "analyze-onboarding-quiz" for onboarding
  "sections": [
    {
      "name": "Section Name",
      "handle": "section_handle",
      "questions": [
        {
          "handle": "question_handle",   // snake_case, globally unique
          "type": "boolean|multiple_choice|scale|freeform",
          "question": "...",
          "filterQuestion": "other_handle",  // optional: skip logic trigger
          "filterValues": ["N"],             // optional: skip when filter has any of these values
          "autoFill": "quiz",                // optional: auto-populate source (null|"quiz"|""|"custom query")
          "evalHint": "...",                  // optional: guidance for the analysis bot on how to interpret this answer
          "maxScore": 7|10,
          "weight": 1-5,
          "answers": [...]               // omitted for freeform
        }
      ]
    }
  ]
}

Question types

TypemaxScoreAnswers
boolean7Exactly 2: “Yes, …” (score 7 or 1) / “No, …” (score 1 or 7)
multiple_choice103-5 options, monotonic scores, ordered best→worst
scale10Exactly 2 anchors: value “0” and value “10”; scores depend on direction (see spec)
freeform10None (qualitative)

Key conventions

  1. Higher score = always better
  2. ~20% freeform (16–22% band), ≥1 per section
  3. Boolean labels start with “Yes”/“No”; comma for continuation (never dashes)
  4. Scale anchors always value “0” and value “10”; scores depend on direction (positive: 0→0, 10→10; inverse: 0→10, 10→0)
  5. MC scoring monotonic in listed order
  6. Smart typography — typographic quotes ” ” and apostrophes ’
  7. Handles snake_case, globally unique across all quizzes
  8. Ordering — booleans before freeform within sections
  9. No compound constructs — split multi-concept questions
  10. Verb consistency — question and answer labels use same verb

Branching logic (skip/auto-fill)

Questions support conditional display and auto-population via three optional fields:

FieldTypePurpose
filterQuestionString?Handle of the question that controls this question’s visibility
filterValuesString[] (default [])If the filter question’s answer matches any of these values, skip this question. An empty array [] means “skip if the filter question has any value at all” (used for back-fill).
autoFillString?Source for auto-population: null = no auto-fill, "quiz" = from previous quiz result, "" = from factoid memory using the question text, "any other string" = from factoid memory using that string as lookup

Critical rules:

  1. Only freeform questions may have autoFill. Boolean, scale, and MC questions must never have autoFill.
  2. Filtered questions must immediately follow their gate question. A boolean gate and its dependent freeform must be adjacent — no other questions between them.
  3. The QC checker’s “booleans before freeforms” warning is overridden by rule 2. When a boolean gate has a dependent freeform, the freeform comes right after the boolean even though it breaks the general ordering guideline. This is an accepted deviation.

How skipping works:

  • When a question is skipped, it is answered with the max score without being displayed
  • For freeform questions, the max-score answer is "N/A"

How back-fill works: When a freeform has autoFill and a stored value exists, the system can skip the boolean gate entirely. The boolean gate declares this by setting filterQuestion to the freeform’s handle with filterValues: [] (empty = any value triggers skip). This creates a bidirectional pair:

  • Boolean → freeform: filterQuestion: "gate_boolean", filterValues: ["N"] — skip freeform when gate is “No”
  • Freeform → boolean (back-fill): Boolean has filterQuestion: "the_freeform", filterValues: [] — skip boolean when freeform already has a value from auto-fill

Common patterns:

Boolean → freeform with auto-fill (full pattern)

{
  "handle": "have_medical_conditions",
  "type": "boolean",
  "filterQuestion": "describe_medical_conditions",
  "filterValues": [],
  ...
},
{
  "handle": "describe_medical_conditions",
  "type": "freeform",
  "filterQuestion": "have_medical_conditions",
  "filterValues": ["N"],
  "autoFill": "",
  ...
}

Flow: If factoid memory has a stored description → skip the boolean (back-fill as “Y”), show freeform for confirmation. If no stored value → ask boolean, then conditionally show freeform.

Boolean → freeform without auto-fill (simple skip)

{
  "handle": "track_health_metrics",
  "type": "boolean",
  ...
},
{
  "handle": "list_tracking_methods",
  "type": "freeform",
  "filterQuestion": "track_health_metrics",
  "filterValues": ["N"],
  ...
}

No back-fill — just skip the freeform when the gate is “No”.

Values filter

A low-importance rating on a values question can skip dependent questions. Use filterValues: ["0","1","2","3"] to skip when importance ≤3.

Values vs. state questions: Some questions measure what a person values rather than how they’re doing. When a values question (e.g., “How important is community to you?”) is used as a filter, respondents who don’t value that area should not be penalized. Use filterQuestion/filterValues to skip dependent questions and auto-fill them at a neutral-to-positive score. This way the quiz adapts to the person’s reality.

Running the QC checker

# Navigate to scripts directory
cd "{PRODUCT}/Domain_Quiz/Scripts"

# Basic check (single file)
npx tsx uvilo_quiz_qc.ts /path/to/quiz.json

# Multiple files with cross-file duplicate handle detection
npx tsx uvilo_quiz_qc.ts /path/to/*.json

# With schema validation
npx tsx uvilo_quiz_qc.ts --schema ./UploadQuizSchema.ts /path/to/quiz.json

# With autofix for typography
npx tsx uvilo_quiz_qc.ts --autofix-typography --write /path/to/quiz.json

# With CSV summary export
npx tsx uvilo_quiz_qc.ts --csv summary.csv /path/to/*.json

# Custom weight/maxScore bounds
npx tsx uvilo_quiz_qc.ts --weight-min 1 --weight-max 5 --maxscore-min 1 --maxscore-max 10 /path/to/quiz.json

Workflow: Review existing quiz

  1. Read {PRODUCT}/Domain_Quiz/LifeDomainQuiz_Spec.md for full rules
  2. Run uvilo_quiz_qc.ts on the quiz file
  3. Review each WARN/ERROR
  4. Cross-reference quiz content against domain definition in LifeDomainData.json
  5. Tag coverage check: verify every tag in the domain’s tags array is addressed by at least one question
  6. Check question coverage against taxonomy entries for the domain in Taxonomy.csv
  7. Report findings without making changes

Workflow: Update existing quiz

  1. Follow review workflow above
  2. Apply fixes per rules in LifeDomainQuiz_Spec.md
  3. Preserve existing handles
  4. Bump version number and update date
  5. Re-run uvilo_quiz_qc.ts to verify
  6. Ensure freeform share is 16–22%

Workflow: Create new quiz

  1. Read domain definition from LifeDomainData.json
  2. Tag coverage: ensure every tag in the domain’s tags array is addressed by at least one question
  3. Review taxonomy entries for the domain in Taxonomy.csv
  4. Follow all conventions in LifeDomainQuiz_Spec.md
  5. Use globally unique handles (check against existing quizzes)
  6. Target ~25-35 questions across 5-7 sections (hard max: 40)
  7. Run uvilo_quiz_qc.ts to validate
  8. Name file as NN_domain_v3.json matching domain number

Domain-to-quiz mapping (current state)

#DomainQuiz fileStatus
1Body01_body.jsonExists
2Mind02_mind.jsonExists
3Home03_home.jsonExists
4Intimacy04_intimacy.jsonExists
5Community05_community.jsonExists
6School06_school.jsonExists (conditional)
7Work07_work.jsonExists
8Business08_business.jsonExists (conditional)
9Money09_money.jsonExists
10Parenting10_parenting.jsonExists (conditional)
11Caregiving11_caregiving.jsonExists (conditional)
12Play12_play.jsonExists
13Growth13_growth.jsonExists
14Purpose14_purpose.jsonExists
Onboardinguvilo_onb_general.jsonExists