{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://www.your-applications.com/schemas/workout-pack-manifest.json",
  "title": "Your Trainer pack manifest (inside .ytwpack)",
  "description": "Per-pack manifest carried inside every .ytwpack archive as `manifest.json` at the archive root. Carries the install-decision payload the app needs to render the pack-install sheet without a second network round-trip (D1 type-mix donut, D2 duration histogram, D3 contents sparkline grid, total ride time, experience level, HRM-required flag) plus the full per-workout content list. Per-workout entries are a superset of the library-manifest's per-workout entry shape — same snake_case fields, all optional pack-specific extras additive.",
  "type": "object",
  "required": [
    "schema_version", "pack_id", "name", "version", "content_hash", "generated_at",
    "set", "category", "workout_count", "total_ride_time_seconds",
    "experience_level", "hrm_required", "type_mix", "duration_histogram", "contents"
  ],
  "additionalProperties": false,
  "properties": {
    "schema_version": {"type": "integer", "const": 1},
    "pack_id": {"type": "string", "description": "Kebab-case stable identifier (e.g. 'sweet-spot')."},
    "name": {"type": "string", "description": "Display name (e.g. 'Sweet Spot')."},
    "description": {"type": "string"},
    "version": {"type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+(?:-[A-Za-z0-9.-]+)?$", "description": "Pack version in SemVer form (MAJOR.MINOR.PATCH, optional -prerelease). Increment on any content change so the app's version-aware updater (FEAT-0081 REQ-0173) surfaces it. Patch bumps for content fixes / regenerator changes; minor for additive workouts; major for breaking schema changes."},
    "content_hash": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$", "description": "SHA-256 over the sorted-by-slug concatenation of each .ytw file's bytes. Stable across regenerations of unchanged content."},
    "generated_at": {"type": "string", "format": "date-time"},
    "set": {"type": "string", "enum": ["power", "hr-zone"]},
    "category": {"type": "string"},
    "workout_count": {"type": "integer", "minimum": 1},
    "total_ride_time_seconds": {"type": "integer", "minimum": 1},
    "experience_level": {"type": "string", "enum": ["beginner", "intermediate", "advanced", "mixed"], "description": "Computed from the overall difficulty range across contents: <=3 → beginner, 4-6 → intermediate, >=7 → advanced, range >4 → mixed. Lowercase wire format; UI is responsible for display capitalisation."},
    "hrm_required": {"type": "boolean", "description": "True if any workout in the pack uses HR_ZONE."},
    "type_mix": {
      "type": "object",
      "description": "Percentage of total_ride_time per category. Sums to 100. Drives the FEAT-0081 D1 donut.",
      "additionalProperties": {"type": "integer", "minimum": 0, "maximum": 100}
    },
    "duration_histogram": {
      "type": "object",
      "description": "Workout count per duration bin. Drives the FEAT-0081 D2 histogram.",
      "additionalProperties": {"type": "integer", "minimum": 0},
      "properties": {
        "0-30": {"type": "integer", "minimum": 0},
        "30-60": {"type": "integer", "minimum": 0},
        "60-90": {"type": "integer", "minimum": 0},
        "90+": {"type": "integer", "minimum": 0}
      }
    },
    "contents": {
      "type": "array",
      "description": "Full per-workout entries. Same snake_case shape as the library-manifest workout_entry plus the sparkline (which drives the FEAT-0081 D3 grid).",
      "items": {"type": "object"}
    }
  }
}
