## ADDED Requirements ### Requirement: Pack layout A course pack SHALL be a zip archive whose root contains `manifest.json` and a `media/` directory with the media files required by that course. An optional cover image MAY be omitted in MVP. #### Scenario: Minimal pack contents - **WHEN** a pack is produced for a valid combo course - **THEN** extracting the zip yields `manifest.json` and the media files referenced by the manifest under `media/` ### Requirement: Manifest schema The `manifest.json` SHALL include `schemaVersion`, stable course `id`, `title`, `mode` (`combo`), `updatedAt`, `items` (album items compatible with desktop combo semantics), and a `resources` array mapping resource ids to relative media paths and kinds (`video` | `audio` | `pdf`). #### Scenario: Resources cover all item references - **WHEN** a manifest lists combo items with resource ids - **THEN** every referenced resource id appears in `resources` with a path that exists inside the pack ### Requirement: iPad local course storage After a successful download, the iPad app SHALL store the unpacked pack under an app-sandbox courses directory keyed by course id and SHALL persist a local course record so the course appears as downloaded. #### Scenario: Downloaded course is local - **WHEN** a pack is downloaded and validated - **THEN** `manifest.json` and media exist under the local courses directory for that id AND a local course record is available to the home deck ### Requirement: Reject invalid packs The iPad app MUST NOT register a local course when `manifest.json` is missing, unreadable, or fails basic validation (missing id/title/items/resources), or when the zip cannot be extracted successfully. #### Scenario: Corrupt or unreadable zip rejected - **WHEN** a download completes but the archive cannot be unzipped or lacks a valid manifest - **THEN** the app does not add a home-deck course and surfaces an error ### Requirement: Update existing course When downloading a course whose id already exists locally with a newer `updatedAt`, the app SHALL replace the local pack contents and update the local course record. #### Scenario: Newer pack overwrites - **WHEN** the user downloads course id X and the pack `updatedAt` is newer than the local copy - **THEN** local media and manifest for X are replaced with the new pack contents