# 1.16.0 — caption position goes native (`import-captions --transform-y`)

Minor release. `import-captions` learns **where to put the captions**: the new
`--transform-y <n>` flag sets `clip.transform.y` on every rebuilt caption segment,
which used to be hardcoded to the screen centre (`{x:0, y:0}`). Output is
**byte-identical** whenever the flag is not used.

## The problem it solves

`import-captions` REPLACES the text track and rebuilds each segment at transform
`{0,0}` — centre screen. Pipelines that want captions lower (TikTok mi-bas /
lower-third) had to re-pin `transform.y` AFTER the import with an orchestrator-side
patch step (Niche_PC's step 3b `restore_caption_transform`), or order the chain so
a final `restyle` re-grafts the position from a gabarit. With the position native
to the import, the re-pin step disappears and import-first chains keep their
position without a trailing restyle.

## Highlights

- 📍 **`import-captions --transform-y <n>`** — every rebuilt caption segment gets
  `clip.transform.y = n`. Global flag only (no per-card field). `transform.x` is
  never touched.
- ➖ **Negatives and zero allowed.** The value is validated as a *finite number*
  (new `parseFiniteFlag`) — unlike the strictly-positive size flags. `-0.4` =
  mi-bas, `-0.6` = lower third, `0` = centre.
- 🧬 **Clone-style aware.** The segment build is shared, so the flag works
  identically with and without `--clone-style`.
- 🛡️ **Validation.** Non-numeric (`abc`), empty (`""`) and whitespace-only values die
  with a clear CliError — the `Number("") === 0` JavaScript coercion trap is guarded
  explicitly. The two malformed spellings that would otherwise degrade to ignored
  positionals are refused as well: `--transform-y=-0.4` (equals syntax) and a
  value-less trailing `--transform-y` both die instead of silently importing your
  captions recentered at y=0 with exit 0.
- 🚩 **Version-gate friendly.** `--transform-y` appears in `--help`, so an
  orchestrator can probe the help text before relying on the flag. This matters:
  a ≤ 1.15.x engine treats unknown flags as ignored positionals and exits 0 —
  your captions would import recentered with no error.
- 🔒 **Byte-identity locked.** A new oracle test freezes the exact v1.15.0
  segment bytes (ids normalized) for the flag-absent path.
- 🧪 **494 tests** (+16). Typecheck clean; lint baseline unchanged.

## Usage

```
# batch captions pinned mi-bas, cloning the draft's existing caption style
$ capcut-david import-captions <draft> captions-styled.json \
    --clone-style --color "#FFFFFF" --transform-y -0.4

# preflight an orchestrator (version gate): is the flag supported?
$ capcut-david --help | grep -q -- "--transform-y" || echo "engine too old"
```

## Compatibility

- No `--transform-y` → **byte-identical** output to 1.15.0 (oracle-locked).
- A later `restyle` grafts the keys present in the preset's `segment` block onto
  every caption segment — a **position-bearing** preset (one whose `segment.clip`
  carries a transform, like the vault gabarits) then **overwrites** the imported y.
  A preset without `segment.clip` (e.g. every `make-preset` output, which emits
  `segment: {}`) leaves the imported position intact. Put the position-bearing
  `import-captions` LAST in the caption chain, or keep carrying the position via
  a clip-bearing restyle preset.
- **One deliberate side effect:** `--transform-y` is parsed globally, so `add-text`
  and `set-text` no longer swallow a literal `--transform-y <n>` into the rendered
  text — the pair is consumed and **ignored** (and a non-numeric next token dies).
  On ≤ 1.15.x the same invocation visibly garbled the caption text. The vertical
  position flag on `add-text` is and remains `--y`.
- No other schema, dependency or behavior change; read commands untouched.
