# Publishing and the resources snapshot

## One repo to read from

The component registry and the Suggested translations are kept in their own resources repo. So that every lookup happens in a single place, a snapshot of both is copied into the specs repo under `resources/`, beside the design export:

```
<specs-repo>/
  resources/
    Figma/Components/<node>.json
    Localization/Suggested/<Key>.json
    Localization/Legacy/<lang>.json
  localizations/<screen-slug>/
    <slug>.md
    <slug>.confluence.xml
    <slug>.overlay.png
    <slug>.keyshots.manifest.json
    <slug>.localization.xlsx
    screenshot.png
    keyshots/   (one keyshot__<NN>__<key>.png per row)
```

### Refreshing it

The refresh has two parts. The first runs locally, the second needs the network:

```bash
scripts/snapshot-resources.sh <resources-repo-root> <specs-repo-root>
python3 scripts/fetch-legacy-labels.py --langs all \
  --out <specs-repo-root>/resources/Localization/Legacy \
  --endpoint '<label-url-with-{lang}>' --headers-file <headers.json>
```

`snapshot-resources.sh` copies `Resources/Figma/Components` and `Resources/Localization/LocalizationStrings/Sources/Suggested` with `rsync -a --delete`; a key or component deleted upstream therefore drops out of the snapshot as well. If either source folder is missing, the script stops with `missing source dir: <dir>`. `fetch-legacy-labels.py` saves a flat `<lang>.json` for every language, sorted by key, and leaves each key exactly as the service stores it, prefix and all. Credential headers belong in `--headers-file` and must not appear on the command line. The refreshed snapshot is committed like any design-export refresh.

### Guardrails

- The skill only adds: its writes are limited to `localizations/` and `resources/`.
- Legacy source, `design-export/` and `specs/` are never modified.
- `resources/` is a generated copy. Refresh it with the scripts; do not edit it by hand.

## Option A: Confluence (live page)

```bash
python3 scripts/publish-confluence.py \
  --xml <slug>.confluence.xml --screen "<Screen name>" \
  --overlay <slug>.overlay.png --screenshot screenshot.png \
  --keyshots <slug>.keyshots.manifest.json \
  --attach <slug>.localization.xlsx
```

- **Target**: `--base` / `--base-url`, `--space` and `--parent`, or the variables `CONFLUENCE_BASE_URL`, `CONFLUENCE_SPACE` and `CONFLUENCE_PARENT`.
- **Title**: `--title` when given; otherwise `<prefix> - <screen>`, with the prefix taken from `--title-prefix`, then `LOCALIZATION_PAGE_PREFIX`, then the default `Localization`.
- **Token**: a Server / Data Center personal access token, sent as a Bearer header. The script reads `CONFLUENCE_API_TOKEN`, then the macOS keychain service given by `--keychain-name` (default `CONFLUENCE_API_TOKEN`). `--token` is refused with exit 2, because other processes can read argv.
- **Transport**: `curl` carries every request. The Authorization header is written to a temporary config file with mode 600, handed over with `-K` and removed at the end; JSON bodies also travel through a temporary file. Each request is bounded: `--connect-timeout 15` and `--max-time 120` for curl, and the script stops waiting for curl after 150 seconds.
- **Idempotent**: the lookup is `GET /rest/api/content?spaceKey=&title=&type=page&expand=version&limit=5`, which reads the database rather than the CQL index. A hit is updated with `PUT` and the next version number, reported as `updated (vN->vN+1)`; a miss is created with `POST` below the parent and reported as `created`. A second run lands on the same page id.
- **Attachments**: the screenshot first, then the overlay, then each `.png` listed in the keyshots manifest, then every `--attach` file. When a file of that name is already attached, a new version is uploaded to `.../child/attachment/<id>/data` instead of adding a second copy.
- **Inline spreadsheet**: an `--attach` file with the extension `.xlsx`, `.xls`, `.csv`, `.docx` or `.pdf` is additionally shown on the page: a heading is appended (`--embed-heading`, default "CMS Import File"; pass the team's own wording, for example a Turkish title, to change it), then one `view-file` macro per such file.
- **Dry run**: with `--dry-run` nothing is sent; the script prints base, space, parent, title, body size, the attachment list and the action it would take.
- **Output**: a JSON object with `action`, `id`, `title`, `attachments` and `url`.

Running `build-artifact.py --print-upload` shows the publish command that fits the mapping, plus a hand-written Bearer `curl` version of it that pulls the token out of the keychain. File paths in the command are joined with `--out` (overlay, screenshot and keyshots manifest are looked up next to the mapping first), every argument is shell-quoted, and the curl config is created under `umask 077`. When neither route works, paste the storage XML into the Confluence editor and attach the images under their file names; the XML already points at them by name.

## Option B: specs-repo pull request

Place the canonical files in `<specs-repo>/localizations/<slug>/`: the Markdown `<slug>.md`, the storage body `<slug>.confluence.xml`, the overlay `<slug>.overlay.png`, `screenshot.png`, the manifest `<slug>.keyshots.manifest.json` with its `keyshots/` folder, and the sheet `<slug>.localization.xlsx`. Then:

```bash
git checkout -b loc-map/<slug>
git add localizations/<slug>
git commit --message "localizations: <slug> reuse map"
git push --set-upstream origin loc-map/<slug>
gh pr create --body "<summary, counts, gate result>" --title "Localization reuse map - <slug>"
```

Because the PR adds files and touches nothing else, it merges without conflicts. Keep the large `.docx` and `.pdf` files out of it and send them to people directly.

A screen may go through both options. Before publishing, ask which option or options to use, the Confluence space, and which formats are wanted.
