{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://wpl.dev/schemas/wpl/v1.schema.json",
  "title": "WPL Wellness Plan",
  "description": "JSON Schema for compiled WPL (Wellness Plan Language) output.",
  "type": "object",
  "required": ["$schema", "version", "plan"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "const": "https://wpl.dev/schemas/wpl/v1.schema.json"
    },
    "version": {
      "type": "string",
      "pattern": "^1\\.[0-9]+\\.[0-9]+$",
      "description": "WPL schema version the plan was authored against. Any 1.x is accepted (additive-only policy); consumers may warn on versions newer than they know."
    },
    "plan": { "$ref": "#/$defs/Plan" }
  },

  "$defs": {
    "Plan": {
      "type": "object",
      "required": ["id", "name", "type", "visibility", "metadata", "goals", "phases"],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-z0-9][a-z0-9_-]*$",
          "description": "Slug or UUID identifier for the plan."
        },
        "name": { "type": "string", "minLength": 1 },
        "description": { "type": "string" },
        "type": { "$ref": "#/$defs/PlanType" },
        "visibility": { "$ref": "#/$defs/Visibility" },
        "metadata": { "$ref": "#/$defs/Metadata" },
        "goals": {
          "type": "array",
          "items": { "$ref": "#/$defs/Goal" }
        },
        "requirements": { "$ref": "#/$defs/Requirements" },
        "personalization": { "$ref": "#/$defs/Personalization" },
        "phases": {
          "type": "array",
          "items": { "$ref": "#/$defs/Phase" }
        },
        "progress": { "$ref": "#/$defs/Progress" },
        "athlete_thresholds": { "$ref": "#/$defs/AthleteThresholds" },
        "notifications": {
          "type": "array",
          "items": { "$ref": "#/$defs/Notification" }
        }
      }
    },

    "AthleteThresholds": {
      "type": "object",
      "description": "Optional reference values for the target athlete that resolve relative-intensity targets (HR zones, %FTP, %1RM) into absolute numbers downstream.",
      "additionalProperties": false,
      "properties": {
        "hr_max_bpm": { "type": "integer", "minimum": 60, "maximum": 250 },
        "lthr_bpm": { "type": "integer", "minimum": 60, "maximum": 250 },
        "resting_hr_bpm": { "type": "integer", "minimum": 30, "maximum": 120 },
        "ftp_watts": { "type": "number", "minimum": 0 },
        "vo2max_ml_kg_min": { "type": "number", "minimum": 0 },
        "critical_pace_seconds_per_km": { "type": "number", "minimum": 0 },
        "body_weight_kg": { "type": "number", "minimum": 0 },
        "one_rm": {
          "type": "array",
          "items": { "$ref": "#/$defs/OneRMEntry" }
        }
      }
    },

    "OneRMEntry": {
      "type": "object",
      "required": ["exercise_ref", "value", "unit"],
      "additionalProperties": false,
      "properties": {
        "exercise_ref": { "type": "string" },
        "value": { "type": "number", "minimum": 0 },
        "unit": { "type": "string", "enum": ["kg", "lb"] }
      }
    },

    "MuscleGroup": {
      "type": "string",
      "description": "Controlled vocabulary for muscle groups. Lets analytics tools compute weekly sets per muscle (Schoenfeld-style volume tracking) without an out-of-band exercise→muscle map.",
      "enum": [
        "chest",
        "upper_back",
        "lats",
        "traps",
        "front_delts",
        "side_delts",
        "rear_delts",
        "biceps",
        "triceps",
        "forearms",
        "abs",
        "obliques",
        "lower_back",
        "spinal_erectors",
        "glutes",
        "quadriceps",
        "hamstrings",
        "calves",
        "hip_adductors",
        "hip_abductors",
        "hip_flexors",
        "neck"
      ]
    },

    "MovementPattern": {
      "type": "string",
      "description": "Functional movement-pattern taxonomy. Useful for balanced-program checks (e.g. push:pull ratio).",
      "enum": [
        "squat",
        "hinge",
        "lunge",
        "push_horizontal",
        "push_vertical",
        "pull_horizontal",
        "pull_vertical",
        "carry",
        "rotate",
        "anti_rotate",
        "gait",
        "jump",
        "isolation"
      ]
    },

    "PlanType": {
      "type": "string",
      "enum": ["workout", "nutrition", "meditation", "recovery", "hybrid"]
    },

    "Visibility": {
      "type": "string",
      "enum": ["private", "public", "template"]
    },

    "Difficulty": {
      "type": "string",
      "enum": ["beginner", "intermediate", "advanced", "adaptive"]
    },

    "TimeUnit": {
      "type": "string",
      "enum": ["seconds", "minutes", "hours", "days", "weeks"]
    },

    "Duration": {
      "type": "object",
      "required": ["value", "unit"],
      "additionalProperties": false,
      "properties": {
        "value": { "type": "number" },
        "unit": { "$ref": "#/$defs/TimeUnit" }
      }
    },

    "Metadata": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "created_at": { "type": "string", "format": "date-time" },
        "updated_at": { "type": "string", "format": "date-time" },
        "created_by": { "type": "string" },
        "tags": {
          "type": "array",
          "items": { "type": "string" }
        },
        "difficulty": { "$ref": "#/$defs/Difficulty" },
        "language": { "type": "string" },
        "estimated_duration_days": { "type": "integer", "minimum": 0 }
      }
    },

    "Goal": {
      "type": "object",
      "required": ["id", "type", "category"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": {
          "type": "string",
          "enum": ["primary", "secondary"]
        },
        "category": {
          "type": "string",
          "description": "Free-form goal category. Recommended values in data/goal-categories.json; unknown values are allowed (validators SHOULD warn, not reject)."
        },
        "name": { "type": "string" },
        "description": { "type": "string" },
        "target": { "$ref": "#/$defs/GoalTarget" },
        "deadline": { "type": "string" },
        "milestones": {
          "type": "array",
          "items": { "$ref": "#/$defs/Milestone" },
          "minItems": 1
        }
      }
    },

    "GoalTarget": {
      "type": "object",
      "required": ["metric", "target_value", "unit", "measurement_type"],
      "additionalProperties": false,
      "properties": {
        "metric": { "type": "string" },
        "target_value": { "type": "number" },
        "unit": { "type": "string" },
        "measurement_type": {
          "type": "string",
          "enum": ["absolute", "relative", "percentage"]
        }
      }
    },

    "Milestone": {
      "type": "object",
      "required": ["id", "name"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "name": { "type": "string" },
        "target_value": { "type": "number" },
        "reward_points": { "type": "number" }
      }
    },

    "Requirements": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "min_age": { "type": "integer", "minimum": 0 },
        "max_age": { "type": "integer", "minimum": 0 },
        "fitness_level": {
          "type": "array",
          "items": { "type": "string" }
        },
        "equipment": {
          "type": "array",
          "items": { "$ref": "#/$defs/Equipment" }
        },
        "contraindications": {
          "type": "array",
          "items": { "$ref": "#/$defs/Contraindication" }
        },
        "time_commitment": { "$ref": "#/$defs/TimeCommitment" }
      }
    },

    "Equipment": {
      "type": "object",
      "required": ["id", "name", "required"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "required": { "type": "boolean" },
        "alternatives": {
          "type": "array",
          "items": { "type": "string" }
        }
      }
    },

    "Contraindication": {
      "type": "object",
      "required": ["condition", "action"],
      "additionalProperties": false,
      "properties": {
        "condition": {
          "type": "string",
          "description": "Free-form condition tag. For clinical use, prefer a recognized prefix so consumers can match deterministically:\n  • 'icd10:<code>' — ICD-10-CM (e.g. 'icd10:O09' = supervision of high-risk pregnancy)\n  • 'snomed:<concept-id>' — SNOMED CT (e.g. 'snomed:248152002' = low back pain)\n  • 'acsm:<category>' — ACSM Guidelines (e.g. 'acsm:cardiac_rehab_phase_2')\n  • 'acog:<id>' — ACOG opinions (e.g. 'acog:pregnancy_t2')\nUnprefixed strings are allowed for non-clinical content (e.g. 'lower_back_injury') but cannot be deterministically matched.",
          "examples": [
            "icd10:O09",
            "snomed:248152002",
            "acsm:cardiac_rehab_phase_2",
            "acog:pregnancy_t2",
            "lower_back_injury"
          ]
        },
        "severity": {
          "type": "string",
          "description": "Optional clinical-risk tier (mirrors the ACSM low/moderate/high stratification). Lets consumers triage required actions: 'low' may proceed with standard plan, 'moderate' may require modification, 'high' typically requires medical clearance before exercise.",
          "enum": ["low", "moderate", "high"]
        },
        "action": {
          "type": "string",
          "description": "What to do when this contraindication applies.\n  • 'exclude' — drop the affected activities from the compiled plan.\n  • 'modify' — replace or down-regulate the affected activities (consumer policy).\n  • 'require_clearance' — gate plan execution on documented medical clearance (consumer is expected to surface a clearance prompt and check 'user.clearance_obtained' before progressing).",
          "enum": ["exclude", "modify", "require_clearance"]
        },
        "affected_activities": {
          "type": "array",
          "items": { "type": "string" }
        }
      }
    },

    "TimeCommitment": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "min_days_per_week": { "type": "integer", "minimum": 1, "maximum": 7 },
        "max_days_per_week": { "type": "integer", "minimum": 1, "maximum": 7 },
        "min_minutes_per_day": { "type": "integer", "minimum": 0 },
        "max_minutes_per_day": { "type": "integer", "minimum": 0 }
      }
    },

    "Personalization": {
      "type": "object",
      "required": ["inputs", "rules"],
      "additionalProperties": false,
      "properties": {
        "inputs": {
          "type": "array",
          "items": { "$ref": "#/$defs/PersonalizationInput" }
        },
        "rules": {
          "type": "array",
          "items": { "$ref": "#/$defs/PersonalizationRule" }
        }
      }
    },

    "PersonalizationInput": {
      "type": "object",
      "required": ["id", "type", "source"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string" },
        "type": {
          "type": "string",
          "enum": ["number", "string", "array", "enum", "boolean"]
        },
        "source": {
          "type": "string",
          "description": "Where this input value comes from. Recognized prefixes:\n  • user.* — static profile fields (user.age, user.experience_level, user.injuries, user.equipment.barbell)\n  • wellness.* — daily/recent telemetry (wellness.sleep_hours_last_night, wellness.hrv_rmssd_morning, wellness.session_rpe_yesterday, wellness.subjective_readiness, wellness.menstrual_phase)\n  • device.* — wearable-derived metrics (device.steps_today, device.battery_drain_24h)\n  • plan.* — plan-state queries (plan.current_phase_type, plan.day_index_in_phase)\nUnrecognized prefixes are allowed but consumers may ignore them.",
          "examples": [
            "user.age",
            "user.experience_level",
            "wellness.hrv_rmssd_morning",
            "wellness.sleep_hours_last_night",
            "wellness.session_rpe_yesterday",
            "device.steps_today",
            "plan.current_phase_type"
          ]
        },
        "label": { "type": "string" },
        "options": {
          "type": "array",
          "items": { "type": "string" }
        }
      }
    },

    "PersonalizationRule": {
      "type": "object",
      "required": ["id", "condition", "actions"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "condition": { "$ref": "#/$defs/Condition" },
        "actions": {
          "type": "array",
          "items": { "$ref": "#/$defs/Action" },
          "minItems": 1
        }
      }
    },

    "Condition": {
      "oneOf": [
        { "$ref": "#/$defs/SimpleCondition" },
        { "$ref": "#/$defs/CompoundCondition" }
      ]
    },

    "SimpleCondition": {
      "type": "object",
      "required": ["field", "op", "value"],
      "additionalProperties": false,
      "properties": {
        "field": { "type": "string" },
        "op": {
          "type": "string",
          "enum": ["eq", "neq", "gt", "gte", "lt", "lte", "contains", "not_contains", "in", "not_in"]
        },
        "value": {}
      }
    },

    "CompoundCondition": {
      "type": "object",
      "required": ["operator", "conditions"],
      "additionalProperties": false,
      "properties": {
        "operator": {
          "type": "string",
          "enum": ["and", "or"]
        },
        "conditions": {
          "type": "array",
          "items": { "$ref": "#/$defs/Condition" },
          "minItems": 1
        }
      }
    },

    "Action": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "forbid_exercise", "exclude_exercise", "replace_exercise", "modify_exercise",
            "modify_intensity", "add_warmup_time", "increase_rest",
            "reduce_sets", "reduce_reps", "use_schedule", "add_activity"
          ]
        },
        "scope": {
          "type": "string",
          "enum": ["activity", "block", "day", "week", "phase", "plan"]
        }
      },
      "additionalProperties": true,
      "allOf": [
        {
          "if": { "required": ["type"], "properties": { "type": { "const": "forbid_exercise" } } },
          "then": {
            "required": ["exercise"],
            "properties": { "exercise": { "type": "string", "minLength": 1 } }
          }
        },
        {
          "if": { "required": ["type"], "properties": { "type": { "const": "replace_exercise" } } },
          "then": {
            "required": ["from", "to"],
            "properties": {
              "from": { "type": "string", "minLength": 1 },
              "to": { "type": "string", "minLength": 1 }
            }
          }
        },
        {
          "if": { "required": ["type"], "properties": { "type": { "const": "modify_intensity" } } },
          "then": {
            "required": ["factor"],
            "properties": { "factor": { "type": "number", "exclusiveMinimum": 0, "maximum": 2 } }
          }
        }
      ]
    },

    "Phase": {
      "type": "object",
      "required": ["id", "name", "order"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "name": { "type": "string" },
        "order": { "type": "integer", "minimum": 1 },
        "type": { "$ref": "#/$defs/PhaseType" },
        "description": { "type": "string" },
        "duration": { "$ref": "#/$defs/Duration" },
        "weeks": {
          "type": "array",
          "items": { "$ref": "#/$defs/Week" }
        }
      }
    },

    "PhaseType": {
      "type": "string",
      "description": "Optional periodization role of a phase. Lets consuming tools surface where in a cycle the user is (e.g. 'Deload week' badges, fatigue plots).",
      "enum": [
        "accumulation",
        "intensification",
        "realization",
        "deload",
        "base",
        "build",
        "peak",
        "recovery",
        "transition"
      ]
    },

    "Week": {
      "type": "object",
      "required": ["id", "name", "order"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "name": { "type": "string" },
        "order": { "type": "integer", "minimum": 1 },
        "is_deload": {
          "type": "boolean",
          "description": "Optional flag marking a deload week. Independent of Phase.type so individual deload weeks can be tagged inside any phase."
        },
        "days": {
          "type": "array",
          "items": { "$ref": "#/$defs/Day" }
        }
      }
    },

    "Day": {
      "type": "object",
      "required": ["id", "day_of_week", "type"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "day_of_week": { "type": "integer", "minimum": 1, "maximum": 7 },
        "type": {
          "type": "string",
          "enum": ["training", "rest", "active_recovery", "assessment"]
        },
        "name": { "type": "string" },
        "estimated_duration_minutes": { "type": "integer", "minimum": 0 },
        "blocks": {
          "type": "array",
          "items": { "$ref": "#/$defs/Block" }
        }
      }
    },

    "Block": {
      "type": "object",
      "required": ["id", "type", "order"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string" },
        "type": {
          "type": "string",
          "enum": ["warmup", "main", "cooldown", "nutrition", "meditation", "education", "assessment"]
        },
        "order": { "type": "integer", "minimum": 1 },
        "structure": {
          "type": "string",
          "enum": ["circuit", "straight_sets", "superset", "emom", "amrap", "tabata"]
        },
        "rounds": { "type": "integer", "minimum": 1 },
        "rest_between_rounds": { "$ref": "#/$defs/Duration" },
        "activities": {
          "type": "array",
          "items": { "$ref": "#/$defs/Activity" }
        }
      }
    },

    "Activity": {
      "oneOf": [
        { "$ref": "#/$defs/ExerciseActivity" },
        { "$ref": "#/$defs/CardioActivity" },
        { "$ref": "#/$defs/NutritionActivity" },
        { "$ref": "#/$defs/MeditationActivity" },
        { "$ref": "#/$defs/RecoveryActivity" },
        { "$ref": "#/$defs/HabitActivity" },
        { "$ref": "#/$defs/SimpleActivityOutput" },
        { "$ref": "#/$defs/SubPlanActivity" }
      ]
    },

    "SubPlanActivity": {
      "type": "object",
      "description": "Activity that includes another plan by reference. Lets a workout reuse a 'warmup plan' or compose larger sessions from smaller plans. Resolution is the consumer's responsibility (validators emit CYCLIC_SUBPLAN if a self-reference or known cycle is detected).",
      "required": ["id", "type", "sub_plan_ref"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "sub_plan" },
        "name": { "type": "string" },
        "sub_plan_ref": {
          "type": "string",
          "description": "Identifier of another WPL plan. Resolution is performed by the consumer; validators only check it's a non-empty string and that the reference is not the containing plan's own id (CYCLIC_SUBPLAN)."
        }
      }
    },

    "ExerciseActivity": {
      "type": "object",
      "required": ["id", "type", "exercise_ref"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "exercise" },
        "exercise_ref": { "type": "string" },
        "name": { "type": "string" },
        "prescription": { "$ref": "#/$defs/ExercisePrescription" },
        "target_rpe": { "type": "number", "minimum": 1, "maximum": 10 },
        "target_rir": { "type": "number", "minimum": 0 },
        "primary_muscles": {
          "type": "array",
          "items": { "$ref": "#/$defs/MuscleGroup" },
          "uniqueItems": true,
          "minItems": 1
        },
        "secondary_muscles": {
          "type": "array",
          "items": { "$ref": "#/$defs/MuscleGroup" },
          "uniqueItems": true
        },
        "movement_pattern": { "$ref": "#/$defs/MovementPattern" }
      }
    },

    "ExercisePrescription": {
      "type": "object",
      "required": ["type"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["sets_reps", "time", "distance", "amrap", "continuous", "intervals"]
        },
        "sets": { "type": "integer", "minimum": 1 },
        "reps": { "$ref": "#/$defs/Reps" },
        "rest": { "$ref": "#/$defs/Duration" },
        "tempo": { "$ref": "#/$defs/Tempo" },
        "to_failure": {
          "type": "boolean",
          "description": "When true, the prescription is performed to momentary muscular failure. Mutually informative with target_rir=0; consumers should treat them as equivalent stop criteria."
        },
        "weight": { "$ref": "#/$defs/Weight" },
        "duration": { "$ref": "#/$defs/Duration" }
      }
    },

    "Reps": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "target": { "type": "integer", "minimum": 1 },
        "min": { "type": "integer", "minimum": 1 },
        "max": { "type": "integer", "minimum": 1 },
        "amrap": {
          "type": "boolean",
          "description": "When true, the set is performed as 'as many reps as possible' to a stop criterion (target_rir, target_rpe, or to_failure). target/min/max may be omitted or used as guidance."
        }
      }
    },

    "Tempo": {
      "description": "Lifting tempo. Either the conventional 4-digit string (e.g. \"3-1-1-0\" or \"30X1\" where X = explosive) for human authoring, or a structured object that tools can compute time-under-tension from. Phases are eccentric (lowering), pause at bottom, concentric (lifting), pause at top — all in seconds.",
      "oneOf": [
        { "type": "string", "minLength": 1 },
        {
          "type": "object",
          "required": ["eccentric", "concentric"],
          "additionalProperties": false,
          "properties": {
            "eccentric": { "type": "number", "minimum": 0 },
            "pause_bottom": { "type": "number", "minimum": 0 },
            "concentric": { "type": "number", "minimum": 0 },
            "pause_top": { "type": "number", "minimum": 0 },
            "explosive_concentric": {
              "type": "boolean",
              "description": "When true, concentric is intent-maximal regardless of the numeric value (corresponds to the 'X' character in 4-digit notation)."
            }
          }
        }
      ]
    },

    "Weight": {
      "oneOf": [
        {
          "type": "object",
          "required": ["type"],
          "additionalProperties": false,
          "properties": {
            "type": { "const": "bodyweight" }
          }
        },
        {
          "type": "object",
          "required": ["type"],
          "additionalProperties": false,
          "properties": {
            "type": { "enum": ["absolute", "percentage_1rm", "percentage_bodyweight"] },
            "value": { "type": "number" },
            "unit": { "type": "string" },
            "metric": {
              "type": "string",
              "description": "Reference metric for percentage_1rm. '1RM' = tested 1-rep max, 'e1RM' = estimated 1RM (formula or velocity-derived), 'training_max' = % of 1RM commonly used as program ceiling (e.g. 90% in 5/3/1), 'daily_max' = today's auto-regulated max. Ignored when type is 'absolute' or 'percentage_bodyweight'.",
              "enum": ["1RM", "e1RM", "training_max", "daily_max"]
            }
          }
        }
      ]
    },

    "CardioActivity": {
      "type": "object",
      "required": ["id", "type", "name", "modality", "prescription"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "cardio" },
        "name": { "type": "string" },
        "modality": { "type": "string" },
        "prescription": { "$ref": "#/$defs/CardioPrescription" }
      }
    },

    "CardioPrescription": {
      "type": "object",
      "required": ["type"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["continuous", "intervals", "fartlek"]
        },
        "duration": { "$ref": "#/$defs/Duration" },
        "intensity": {
          "type": "object",
          "required": ["type"],
          "additionalProperties": true,
          "properties": {
            "type": {
              "type": "string",
              "enum": ["heart_rate_zone", "rpe", "pace", "power", "bpm"]
            },
            "target": {
              "type": "object",
              "additionalProperties": true,
              "description": "Intensity target. Recognized fields by intensity.type:\n  • heart_rate_zone: { zone: integer } or { min_bpm, max_bpm }\n  • bpm: { min_bpm, max_bpm }\n  • power: { zone: integer } or { min_watts, max_watts }\n  • pace: { value: number, unit: 'min_per_km' | 'min_per_mi' | 'm_per_s' | 'sec_per_100m' }\n  • rpe: { min: number, max: number } on the scale matched by zone_model (rpe_borg_10 or rpe_borg_20)",
              "properties": {
                "zone": { "type": "integer", "minimum": 1, "maximum": 7 },
                "min_bpm": { "type": "integer", "minimum": 30, "maximum": 250 },
                "max_bpm": { "type": "integer", "minimum": 30, "maximum": 250 },
                "min_watts": { "type": "number", "minimum": 0 },
                "max_watts": { "type": "number", "minimum": 0 },
                "min": { "type": "number" },
                "max": { "type": "number" },
                "value": { "type": "number" },
                "unit": {
                  "type": "string",
                  "enum": ["min_per_km", "min_per_mi", "m_per_s", "sec_per_100m"]
                }
              }
            },
            "zone_model": {
              "type": "string",
              "description": "Disambiguates which zone numbering system the target uses. Recommended whenever type is heart_rate_zone, power, or pace.",
              "enum": [
                "hr_3_zone_seiler",
                "hr_5_zone",
                "hr_7_zone",
                "power_coggan_7_zone",
                "pace_critical_speed",
                "rpe_borg_10",
                "rpe_borg_20"
              ]
            }
          }
        },
        "intervals": {
          "type": "object",
          "required": ["work", "rest", "repeat"],
          "additionalProperties": false,
          "properties": {
            "work": {
              "oneOf": [
                {
                  "type": "object",
                  "required": ["duration"],
                  "additionalProperties": false,
                  "properties": {
                    "duration": {
                      "type": "number",
                      "description": "Work-bout duration in seconds. Deprecated bare-number form — prefer the {value, unit} object for consistency with the rest of the schema."
                    }
                  }
                },
                {
                  "type": "object",
                  "required": ["duration"],
                  "additionalProperties": false,
                  "properties": {
                    "duration": { "$ref": "#/$defs/Duration" }
                  }
                }
              ]
            },
            "rest": {
              "oneOf": [
                {
                  "type": "object",
                  "required": ["duration"],
                  "additionalProperties": false,
                  "properties": {
                    "duration": {
                      "type": "number",
                      "description": "Rest-bout duration in seconds. Deprecated bare-number form — prefer the {value, unit} object."
                    }
                  }
                },
                {
                  "type": "object",
                  "required": ["duration"],
                  "additionalProperties": false,
                  "properties": {
                    "duration": { "$ref": "#/$defs/Duration" }
                  }
                }
              ]
            },
            "repeat": { "type": "integer", "minimum": 1 }
          }
        }
      }
    },

    "NutritionActivity": {
      "type": "object",
      "required": ["id", "type", "name", "category"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "nutrition" },
        "name": { "type": "string" },
        "category": { "type": "string" },
        "dietary_tags": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Recommended values in data/dietary-tags.json; unknown values allowed (validators SHOULD warn)."
        },
        "prescription": { "$ref": "#/$defs/NutritionPrescription" },
        "timing": { "$ref": "#/$defs/NutritionTiming" }
      }
    },

    "NutritionPrescription": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "macros": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "protein": { "$ref": "#/$defs/MacroRange" },
            "carbs": { "$ref": "#/$defs/MacroRange" },
            "fat": { "$ref": "#/$defs/MacroRange" }
          }
        },
        "calories": {
          "type": "object",
          "required": ["min", "max"],
          "additionalProperties": false,
          "properties": {
            "min": { "type": "number" },
            "max": { "type": "number" },
            "unit": {
              "type": "string",
              "description": "Optional unit qualifier. Defaults to absolute kcal. 'kcal_per_kg' scales by body weight; 'multiplier_of_tdee' lets a plan say 'eat 0.8–1.0× TDEE' independent of athlete-specific TDEE.",
              "enum": ["kcal", "kcal_per_kg", "multiplier_of_tdee"]
            }
          }
        },
        "suggestions": {
          "type": "array",
          "items": { "type": "string" }
        }
      }
    },

    "MacroRange": {
      "type": "object",
      "description": "Macronutrient range. 'g' = absolute grams; 'g_per_kg' = grams per kg of body weight (preferred for evidence-based prescriptions, e.g. 1.6–2.2 g/kg/day protein per Morton 2018).",
      "required": ["min", "max", "unit"],
      "additionalProperties": false,
      "properties": {
        "min": { "type": "number" },
        "max": { "type": "number" },
        "unit": { "type": "string", "enum": ["g", "g_per_kg"] }
      }
    },

    "NutritionTiming": {
      "type": "object",
      "required": ["type"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["relative", "absolute"]
        },
        "reference": { "type": "string" },
        "offset": { "$ref": "#/$defs/Duration" },
        "time": { "type": "string", "pattern": "^\\d{2}:\\d{2}$" }
      }
    },

    "MeditationActivity": {
      "type": "object",
      "required": ["id", "type", "name", "category"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "meditation" },
        "name": { "type": "string" },
        "category": { "type": "string" },
        "prescription": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "duration": { "$ref": "#/$defs/Duration" },
            "guided": { "type": "boolean" }
          }
        }
      }
    },

    "RecoveryActivity": {
      "type": "object",
      "required": ["id", "type", "name", "category"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "recovery" },
        "name": { "type": "string" },
        "category": { "type": "string" },
        "prescription": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "duration": { "$ref": "#/$defs/Duration" },
            "exercises": {
              "type": "array",
              "items": { "$ref": "#/$defs/RecoveryExercise" }
            }
          }
        }
      }
    },

    "RecoveryExercise": {
      "type": "object",
      "required": ["id", "type", "name"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "recovery_exercise" },
        "name": { "type": "string" },
        "modality": {
          "type": "string",
          "description": "Recovery technique. 'static_stretch' = passive hold; 'dynamic_stretch' = active ROM; 'pnf' = proprioceptive neuromuscular facilitation (use the 'pnf' block); 'smr_foam_roll' / 'smr_ball' = self-myofascial release; 'breathwork' = breathing protocol; 'mobility_drill' = active mobility.",
          "enum": [
            "static_stretch",
            "dynamic_stretch",
            "pnf",
            "smr_foam_roll",
            "smr_ball",
            "breathwork",
            "mobility_drill"
          ]
        },
        "hold_seconds": { "type": "number", "minimum": 0 },
        "reps": { "type": "integer", "minimum": 1 },
        "sides": {
          "type": "string",
          "enum": ["both", "left", "right"]
        },
        "intensity_rpe": {
          "type": "number",
          "minimum": 1,
          "maximum": 10,
          "description": "Subjective stretch/work intensity on the 1–10 RPE-style scale. Useful for static_stretch (target ~6–7 for ROM gains) and SMR (target ~5–7)."
        },
        "pnf": {
          "type": "object",
          "description": "PNF contract-relax parameters. Required when modality='pnf'.",
          "required": ["contraction_seconds", "relax_seconds", "contractions"],
          "additionalProperties": false,
          "properties": {
            "contraction_seconds": { "type": "number", "minimum": 0 },
            "relax_seconds": { "type": "number", "minimum": 0 },
            "contractions": { "type": "integer", "minimum": 1 }
          }
        },
        "body_part": {
          "type": "string",
          "description": "Free-text body region (e.g. 'thoracic_spine', 'piriformis'). Use MuscleGroup values where applicable."
        }
      }
    },

    "HabitActivity": {
      "type": "object",
      "required": ["id", "type", "name", "category"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "habit" },
        "name": { "type": "string" },
        "category": { "type": "string" },
        "prescription": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "target": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "value": { "type": "number" },
                "unit": { "type": "string" }
              }
            },
            "frequency": { "type": "string" },
            "reminders": {
              "type": "array",
              "items": { "type": "string" }
            },
            "reminder_times": {
              "type": "array",
              "items": { "type": "string" }
            }
          }
        },
        "tracking": {
          "type": "object",
          "additionalProperties": true
        }
      }
    },

    "SimpleActivityOutput": {
      "type": "object",
      "required": ["id", "type", "name"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "type": { "const": "simple" },
        "name": { "type": "string" },
        "duration": { "$ref": "#/$defs/Duration" }
      }
    },

    "Progress": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "checkpoints": {
          "type": "array",
          "items": { "$ref": "#/$defs/Checkpoint" }
        },
        "points_system": { "$ref": "#/$defs/PointsConfig" },
        "streaks": {
          "type": "object",
          "description": "Streak tracking config; shape TBD in v1.x.",
          "additionalProperties": true
        }
      }
    },

    "Checkpoint": {
      "type": "object",
      "required": ["id", "name"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*$" },
        "name": { "type": "string" },
        "at": {
          "type": "object",
          "required": ["value", "unit"],
          "additionalProperties": false,
          "properties": {
            "value": { "type": "number" },
            "unit": { "type": "string" }
          }
        },
        "measurements": {
          "type": "array",
          "description": "What to measure at this checkpoint. Items can be a free string (back-compat with 1.0–1.5 plans) or a typed MeasurementSpec for analytics-grade comparability.",
          "items": {
            "oneOf": [
              { "type": "string" },
              { "$ref": "#/$defs/MeasurementSpec" }
            ]
          }
        },
        "questions": {
          "type": "array",
          "items": { "type": "string" }
        }
      }
    },

    "MeasurementSpec": {
      "type": "object",
      "required": ["metric"],
      "additionalProperties": false,
      "properties": {
        "metric": { "$ref": "#/$defs/MeasurementMetric" },
        "unit": {
          "type": "string",
          "description": "Optional unit override. Each MeasurementMetric carries an implied canonical unit (see enum description); set this only to override or disambiguate."
        },
        "questionnaire": { "$ref": "#/$defs/Questionnaire" },
        "note": { "type": "string" }
      }
    },

    "MeasurementMetric": {
      "type": "string",
      "description": "Standardized measurement vocabulary. Canonical units in parentheses.\n  • body_weight_kg (kg) • waist_cm (cm) • hip_cm (cm) • body_fat_pct (%) • lean_mass_kg (kg)\n  • resting_hr_bpm (bpm) • hrv_rmssd_ms (ms) • blood_pressure_systolic_mmhg (mmHg) • blood_pressure_diastolic_mmhg (mmHg)\n  • vo2max_ml_kg_min (mL/kg/min) • six_min_walk_m (m) • cooper_test_m (m)\n  • one_rm_kg (kg, requires exercise_ref via note) • grip_strength_kg (kg) • vertical_jump_cm (cm)\n  • sit_and_reach_cm (cm) • shoulder_flexion_deg (deg)\n  • sleep_hours_avg (h) • session_rpe_avg (1-10)\n  • questionnaire_score (numeric, paired with the 'questionnaire' field)\n  • photo (string ref) • free_text (free)",
      "enum": [
        "body_weight_kg",
        "waist_cm",
        "hip_cm",
        "body_fat_pct",
        "lean_mass_kg",
        "resting_hr_bpm",
        "hrv_rmssd_ms",
        "blood_pressure_systolic_mmhg",
        "blood_pressure_diastolic_mmhg",
        "vo2max_ml_kg_min",
        "six_min_walk_m",
        "cooper_test_m",
        "one_rm_kg",
        "grip_strength_kg",
        "vertical_jump_cm",
        "sit_and_reach_cm",
        "shoulder_flexion_deg",
        "sleep_hours_avg",
        "session_rpe_avg",
        "questionnaire_score",
        "photo",
        "free_text"
      ]
    },

    "Questionnaire": {
      "type": "string",
      "description": "Validated self-report instrument when metric is 'questionnaire_score'. Consumers pair the resulting numeric score with this enum to interpret cutoffs.",
      "enum": [
        "phq9",
        "gad7",
        "ipaq_short",
        "ipaq_long",
        "psqi",
        "pss10",
        "borg_cr10",
        "rpe_session"
      ]
    },

    "PointsConfig": {
      "type": "object",
      "required": ["enabled"],
      "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean" },
        "rules": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["action", "points"],
            "additionalProperties": false,
            "properties": {
              "action": { "type": "string" },
              "points": { "type": "number" }
            }
          }
        }
      }
    },

    "Notification": {
      "type": "object",
      "required": ["id", "enabled", "message"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string" },
        "enabled": { "type": "boolean" },
        "message": { "type": "string" },
        "timing_offset": { "$ref": "#/$defs/Duration" },
        "timing_reference": { "type": "string" }
      }
    }
  }
}
