# Pack Merge

Browser tool to merge two or more v2 packs into a single combined pack, interleaving lines by `cueIn` timecode.
**https://contexts.live/org/{slug}/tools/cx-merge.html**

---

## What it does

Takes N packs in a defined order, interleaves their lines by `cueIn` (lines without `cueIn` are appended in source order after timed lines), merges their metadata (leftmost pack wins on conflicts), records provenance in `meta.merged`, and saves the result as a new pack via the server.

---

## Usage

1. Open the tool at `https://contexts.live/org/{slug}/tools/cx-merge.html`
2. Click packs in the **Available packs** list to add them to the **Merge order** queue
3. Reorder with ↑ ↓ — leftmost (top) has priority on meta field conflicts
4. Enter an **Output pack ID** — must not match any source pack or existing pack
5. Click **Merge & save**

The merged pack is saved immediately to `org/{slug}/packs/{id}.json`. Open in the editor to review or adjust.

---

## Merge rules

### Lines
- All lines from all source packs are collected and sorted by `cueIn`
- Lines without `cueIn` are appended after timed lines, in source order
- No deduplication — all lines are preserved
- Lines from different packs with the same `cueIn` are kept as separate lines (different fields, different elements — no conflict)

### Meta
- All meta fields are merged into one block
- Leftmost pack wins where the same field appears in more than one pack
- `id` is always the user-supplied output ID
- `meta.merged` records provenance — see below

---

## Provenance — `meta.merged`

The merged pack records which fields each source pack contributed:

```json
{
  "meta": {
    "id": "BlB_full",
    "title": "Bluebeard's Castle",
    "language": "en",
    "merged": [
      { "id": "BlB",       "title": "Bluebeard's Castle", "language": "en", "fields": ["st", "sp", "ad"] },
      { "id": "BlB_score",                                                   "fields": ["score"] }
    ]
  }
}
```

This is primarily for auditing. Unmerging is possible by extracting lines containing the listed fields, but is not a guaranteed contract if fields overlap across sources.

---

## Live merge (runtime)

A planned future feature will allow packs to declare their merge sources in `meta.merge`:

```json
{
  "meta": {
    "id": "BlB",
    "merge": ["BlB_score", "BlB_ad"]
  }
}
```

At pack load time, the runtime will fetch and interleave the listed packs in memory — no pre-processing needed. The base pack's ID is preserved; no new ID required.

---

## Multi-pack pages and `?pack=` anchor

When a display page has both pinned panes (`pack=X` on a pane element) and un-pinned panes, the `?pack=` URL parameter anchors the un-pinned panes:

- **`?pack=BlB` set** — `state.packName` is locked to `BlB` for the page lifetime. Cues for other packs update pinned panes only; un-pinned panes stay on `BlB`.
- **No `?pack=`** — `state.packName` follows incoming cues, switching pack as needed.

See cx-spec.md for full detail.

---

## Notes

- Merged packs are marked with a **merged** badge in the tool's pack list
- The tool loads all packs to detect the merged badge — this may be slow for large pack lists
- The output pack ID must be unique — the tool will reject IDs that already exist
- After merging, open the pack in the editor to verify line order and content
