Current section
Files
Jump to
Current section
Files
priv/schema/v1.schema.json
{
"$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",
"const": "1.6.0"
},
"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" },
"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"]
},
"value": {}
}
},
"CompoundCondition": {
"type": "object",
"required": ["operator", "conditions"],
"additionalProperties": false,
"properties": {
"operator": {
"type": "string",
"enum": ["and", "or"]
},
"conditions": {
"type": "array",
"items": { "$ref": "#/$defs/SimpleCondition" },
"minItems": 1
}
}
},
"Action": {
"type": "object",
"required": ["type"],
"properties": {
"type": {
"type": "string"
},
"scope": {
"type": "string"
}
},
"additionalProperties": true
},
"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" },
"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" }
}
}
}
}