# Multi-image task collections

Use task collections when one user action creates an ordered image set whose images share source
assets and generation settings but have independent prompts. Star Task orchestrates the set while
ordinary child tasks remain the unit of provider execution, charging, settlement, and refunds.

## Contract

- Upload 1–4 source images through `/v1/upload` first.
- Plan titles, prompts, and order in the site UI before submission. `items[]` order becomes the
  persisted `position`; Star Task does not write product copy or reorder a confirmed plan.
- `ecommerce-main` accepts 1–5 items. `ecommerce-detail` accepts exactly 6 or 8 items.
- Select the exact image mode from the runtime model catalog. Do not hard-code a provider or price.
- Quote the complete set with `POST /v1/task-collections/quote`, then create it with
  `POST /v1/task-collections` using a fresh UUID v4 `idempotency_key`.
- Persist the returned collection ID. Restore work with `GET /v1/task-collections/:id`, or discover
recent sets with `GET /v1/task-collections` after login.
- If a global creation feed also consumes `GET /v1/tasks?include=summary`, child rows expose
  `collection_id`, `collection_item_id`, and `collection_version`; group or suppress those rows to
  avoid showing the same set once as a collection and again as unrelated images.

```js
const plan = {
  site_id: SITE_ID,
  collection_type: 'ecommerce-detail',
  model: selectedMode.task_model,
  task_type: selectedMode.task_type,
  shared_input: {
    image_urls: uploadedUrls,
    product_description: productDescription,
    size: selectedSize,
    resolution: selectedResolution,
    quality: selectedQuality,
  },
  items: panels.map((panel, index) => ({
    client_item_id: panel.stableId,
    title: panel.title,
    prompt: panel.prompt,
    input: panel.overrides ?? {},
  })),
};

const quote = await api('/v1/task-collections/quote', {
  method: 'POST',
  body: JSON.stringify(plan),
});

const created = await api('/v1/task-collections', {
  method: 'POST',
  body: JSON.stringify({ ...plan, idempotency_key: crypto.randomUUID() }),
});
```

The quote returns `total_cost_points` and a cost for every `client_item_id`. The collection create
response is authoritative. Each child is charged immediately before it is queued. A failed or
canceled child uses the existing single-task refund path. If points run out partway through a set,
successful and active children remain untouched and unsubmitted children are paused.

## Status and controls

Poll `GET /v1/task-collections/:id` every 3–5 seconds while the collection is active. Render each
item's `status`, `current_task_id`, `versions`, `cost_points`, `net_cost_points`, `output`, `error`,
and `error_code`. `versions[0]` is the newest version. Output URLs are Star Task durable URLs.

- `POST /v1/task-collections/:id/stop` cancels and refunds only children that are still pending and
  marks unsubmitted children canceled. Already running children finish normally.
- `POST /v1/task-collections/:id/resume` with a new action `idempotency_key` submits only unfinished,
  failed, or canceled items. It never recreates a successful current version.
- `POST /v1/task-collections/:id/items/:itemId/regenerate` with a new action
  `idempotency_key` creates a new version and preserves every old version. Do not call it while the
  current version is pending or running.

The site owns preview layout, selection, and ZIP assembly. Include only successful Star Task output
URLs in a ZIP. A collection failure does not authorize frontend point writes or refunds.

## Recovery and isolation

Reuse the original creation idempotency key only for a retry of the same confirmed set. Use a new
key for each user-triggered resume or regeneration and reuse that key only for a network retry of
that action. Star Task scopes every collection lookup to the authenticated user and site.

Ordinary uploaded reference images use the temporary upload lifecycle. When a collection is created,
Star Task promotes its own `/v1/assets/...` reference images to a durable input prefix while keeping
the same public URLs. This allows a paused set to resume after the normal two-hour upload window.
External CDN URLs remain the site's responsibility. Generated results use the normal durable
task-result storage.
