---
name: coa-cli
description: Run CoAnimator from the terminal — create projects on disk, edit timelines, render to MP4 with the `coa` CLI, and pack/unpack `.coa` packages, using the bundled docs as the source of truth. Trigger when the user asks to "render", "export", "encode", or "produce an mp4 of" a CoAnimator project, wants to create or scaffold a CoAnimator project without the UI, wants to batch / script / automate exports, wants to package a project as a `.coa` file or extract one, or asks to install or troubleshoot the `coa` CLI itself.
version: 2.1.0
author: CoAnimator
user-invocable: true
argument-hint: "[project id, path, or .coa file] [--resolution 1080p] [--fps 30] [--quality high] [--out path.mp4] [--timeline id|name]"
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(coa *), Bash(which *), Bash(where *), Bash(ls *), Bash(cat *), Bash(mkdir *), Bash(open *), Bash(ffprobe *), Bash(xvfb-run *)
---

# coa-cli — CoAnimator from the terminal

Export recovery: a lost graphics context is a failed frame, not a black frame to
encode. Preserve the current output and report the graphics error. Ask the user
to retry after reducing graphics load or updating the driver; do not alter scene
timing or project data to conceal the failure.

CoAnimator's built-in agent file edits preserve the full UTF-8 file (up to 8 MiB) and replace it
atomically. Preview truncation is never input for a rewrite. Paths must stay
within the real project directory, including through symlinks or junctions.

During app updates, let pending timeline/workspace writes and package saves
finish before restarting. An update paused for an active job or failed save
must not be forced through by launching the installer directly.

Release packaging validates the installed builder schema and current What's New
notes first. Mac update ZIPs must preserve signed framework symlinks: the build
uses `ditto`, compares the extracted bundle to the signed app, verifies its code
signature, then regenerates the blockmap and manifest hashes. A working DMG does
not prove the ZIP can update an installed app. The Mac updater manifest also
includes Darwin eligibility; all installer hashes are checked before upload. Use the package build scripts
so these steps run; keep beta drafts unpublished until the owner approves them.
Manually installed beta builds default to beta updates unless a channel was
explicitly saved. Leaving beta waits for a newer stable release; never recommend
an older production installer as an update. Draft installers are not public
updater offers. Linux package downloads must match the offered version and arch.
Linux ARM64 uses `beta-linux-arm64.yml` or `latest-linux-arm64.yml`; never use an
x64 feed or invent a DEB link when the package is absent from the manifest.
A feed-routing repair must be deployed to the website as well as shipped in the
app. Old installed clients retain their old channel logic until updated.
Release CI requires real FFmpeg/FFprobe tests (`COA_REQUIRE_MEDIA_TESTS=1`), using
the shared media-test setup action. Missing binaries must fail CI, not skip the
encode checks. These dependencies belong to disposable runners, not user setup.
The announcement bar carries plain-text website notices and HTTPS links; it
never authorizes a command or forced installation. See `docs/app-announcements.md`
for targeting, expiry and cache rules. Do not hand-edit its app-owned state or
mistake a notice for proof that an update was published or installed.

CoAnimator is a desktop studio, but the full project lifecycle works from a terminal: projects are plain folders the app discovers by scanning disk, timeline edits hot-reload into any open editor window within ~200 ms, and the `coa` CLI renders the same chunk-parallel pipeline as the desktop **Export** modal — shared Standard export functions; exact encoded bytes can differ across platforms and settings.

**This skill is a router, not a reference.** The authoritative docs ship WITH the app. Read the matching doc before working in its area; never trust flag tables from memory (including the ones below — they are conveniences, the doc wins on conflict).

## Runtime requirements

Agent troubleshooting: a standalone Windows Claude install is supported; do not
replace it with npm just because chat launch failed. The catalog and chat share
executable detection. A quoted PowerShell executable needs `&` to run, for example
`& 'C:\Users\ASD\.local\bin\claude.exe'`. CoAnimator now formats terminal launches
for the actual shell and escapes npm shims separately. A root chat has no
`project.json`; choose/open a project before assuming its metadata exists. Missing
chat folders should report an error rather than fall back to another folder.
See `docs/agent-chat.md` for Windows launch and file-search regression checks.

The Electron 44 desktop app and bundled `coa` CLI require **macOS 13 Ventura or later**. Windows and Linux builds target 64-bit systems. For source builds, use Node 24 and the branch’s locked dependencies; rebuild node-pty for the installed Electron version. See `docs/cli.md` for the installed command and export contract.

## Step 0 — find the docs and the projects

Both live under the app's data dir (seeded on first launch):

| Platform | Docs | Projects |
|---|---|---|
| macOS | `~/Library/Application Support/CoAnimator/docs/` | `~/Library/Application Support/CoAnimator/projects/` |
| Linux | `~/.config/CoAnimator/docs/` | `~/.config/CoAnimator/projects/` |
| Windows | `%APPDATA%/CoAnimator/docs/` | `%APPDATA%/CoAnimator/projects/` |
| Dev (running from the repo) | `<repo>/docs/` | `<repo>/projects/` |

Task → doc:

| Before you… | Read |
|---|---|
| Touch any timeline JSON | `docs/timeline.md` — tracks/clips schema, animation segments, safe-write rules, `__kcControl` player contract |
| Use `coa render` / `pack` / `unpack` flags | `docs/cli.md` — the full CLI reference |
| Handle a `.coa` file | `docs/coa-format.md` — zip layout, manifest, sidecar, save semantics |
| Reason about the launcher, archive, delete, collections | `docs/project-management.md` |
| Work with templates | `docs/templates.md` |
| Troubleshoot chat connection, model selection or browser sign-in | `docs/agent-chat.md` — guided setup, installation, transient device-code card and explicit model choice |
| Run the video-production pipeline (brief → script → voice) | `docs/video-workflow.md` |

Google setup uses the Antigravity terminal option. Legacy Gemini chats must
never retry retired consumer OAuth. See `docs/agent-chat.md`.

## Step 1 — verify coa is installed

macOS/Linux: `command -v coa`. Windows PowerShell: `Get-Command coa -ErrorAction SilentlyContinue`; Command Prompt: `where coa`.

If missing, open **Settings → General → Command line & agent skill → Install coa command**, or **CoAnimator → Install coa Command Line Tool…**. This works on Windows, macOS and Linux. Windows uses a per-user `coa.cmd` and user PATH; macOS uses `/usr/local/bin`; Linux may use `~/.local/bin`. Open a new terminal after installing. Already-open terminal apps may need restarting to inherit PATH.

No standalone Node.js, npm, Bash or WSL is required for the packaged CLI. FFmpeg/FFprobe are required for rendering. Do NOT suggest `npm install` or `brew install` for coa — neither exists.

Windows example:

```powershell
coa render "C:\Projects\My animation" --preset 720p30 --out "C:\Videos\My export.mp4"
```

Without PATH setup, the actual installed `CoAnimator.exe` accepts `--cli render …`. In PowerShell prefix a quoted executable path with `&`. Confirm the real install location; do not guess it from the username or assume `C:\Program Files`. The packaged `resources/coa.cmd` is another entry point. For manually entered PowerShell commands with shell metacharacters (such as `&` or `|`) in an argument, prefer the direct `CoAnimator.exe --cli` invocation: legacy PowerShell can strip quotes when invoking batch files. CoAnimator-generated terminal commands handle this automatically.

Linux AppImage users can invoke `./CoAnimator-<version>.AppImage --cli render <project>` directly. Dev: run `pnpm exec electron . --dev --cli render <project>` from the repository; Unix also supports `./bin/coa`.

The app automatically seeds this skill into its Skills page. **Install agent skill** optionally installs it for Claude Code (`~/.claude/skills/coa-cli`) or Codex CLI (`~/.agents/skills/coa-cli`) at user scope. Managed copies update when the packaged app opens. After updating the app, start a new agent session to load the refreshed instructions. Updates preserve custom edits, disabled app skills, and deleted global skills. The explicit Restore action (or a requested session that requires the skill) can re-enable the bundled app skill. The coa command also works without this skill. The command runs the updated app executable; the skill supplies instructions, not a separate runtime. Managed Windows launchers and unmodified CoAnimator AppImage wrappers repair on GUI launch after updates/moves; macOS and DEB links follow normal updates in the same install location. Explicit reinstall backs up custom/legacy copies. Installing the skill and installing the `coa` command are separate actions. Do not modify user agent credentials or replace their agent installation.

## Step 2 — find or create the project

`coa render` accepts a **project id** (resolved against the projects dir above), a **folder path** containing `project.json`, or a **`.coa` file**. To list ids: `ls` the projects dir. If the user named a project loosely ("the widget one"), `ls | grep -i widget`.

### Creating a project from the terminal

The launcher's project list is a pure disk scan — any folder in the projects dir with a valid `project.json` appears on the next launch/Refresh, and `coa render` picks it up immediately. To scaffold a blank schemaVersion-2 project:

```
projects/<id>/
  project.json          ← metadata (below)
  index.html            ← animation exposing window.__kcControl (docs/timeline.md → Player contract)
  timelines/main.json   ← track/clip data (docs/timeline.md)
  timeline.json         ← copy of main.json + "_mirrorOf": "main" (app regenerates it after)
```

Minimal `project.json` (folder name must equal `id`):

```json
{
  "id": "<id>", "name": "<Display Name>", "sourceType": "inapp",
  "duration": 30, "width": 1920, "height": 1080,
  "videoFile": "index.html",
  "createdAt": "<iso>", "updatedAt": "<iso>",
  "schemaVersion": 2,
  "timelines": [{ "id": "main", "name": "Main", "file": "timelines/main.json", "animation": "index.html" }],
  "activeTimelineId": "main"
}
```

Minimal `timelines/main.json`: `{ "version": 1, "duration": 30, "fps": 30, "animationStart": 0, "animationDuration": 30, "tracks": [...], "annotations": [] }` — first track is always the locked `track-video` (the animation, empty `clips`). Copy the track scaffold from `docs/timeline.md`; do not invent shapes.

Template-based creation (scene-explainer, 3d-explainer, …) stays in the app's **New** picker — templates install on demand from the registry and are not on disk until then.

### Editing and "saving"

Folder edits are saved on disk and hot-reload in open editor windows within ~200 ms. For a package-backed project, **Save Package** also repacks the working copy into the `.coa` document; window close auto-repacks dirty packages. See `docs/coa-format.md` before resolving package/working-copy conflicts. Rules from `docs/timeline.md` (read it first):

- Prefer `timelines/<id>.json`; root `timeline.json` also works for the active timeline (write-through mirror, newer file wins).
- Write via a sibling `*.tmp` file + rename, so the watcher never reads torn JSON.
- Carry the existing `rev` forward unchanged; the app bumps it, not you.
- Roll back by copying a snapshot from `timelines/.checkpoints/` over the timeline file. Never write into `.checkpoints/`.
- Check `annotations[]` for position-anchored "Note for AI" requests; remove a note in the same write that resolves it.
- Interactive players can opt in with `window.__kcControl.interactive = true` at registration. Stage clicks/drags then reach the scene; Frame tools can pause interaction, and annotation capture always takes priority. Keep `seekAndPause` deterministic by clearing live input state. See `docs/editor.md`.
- Stage image capture copies PNG only; users copy timestamp/context separately. Hover over the paused stage area to reveal its lower-right capture control; keyboard focus also reveals it, and active region selection keeps it visible. A pasted `CoAnimator frame reference` identifies the timeline file, project timestamp, mapped animation time, and any numbered image crops. Inspect the attached image with your media tools and use the named timeline; the reference alone is not a request to edit. Capture details live in `docs/editor.md` under **Frame references and annotation capture**.
- Never edit `projects.json`, `.coa-state.json`, or other app-owned state (see the repo/app `CLAUDE.md` never-edit list).

Archive, delete, rename, duplicate, and import stay in the desktop launcher (`docs/project-management.md`).

## Step 3 — render

```sh
coa render <project> [--resolution 720p|1080p|4k] [--fps 24] [--quality low|standard|high|max] [--start 1] [--end 5] [--workers auto] [--out path.mp4] [--timeline id|name]
```

Defaults: 720p for Free, 1080p for Pro, selected timeline FPS (otherwise 30),
Standard quality, Auto workers and Standard engine. Legacy `--preset 720p30`,
`720p60`, `1080p30`, `1080p60`, `4k30` and `4k60` remain supported. Explicit
`--resolution` and `--fps` override the corresponding preset field.

Quality changes compression only. Preserve the requested FPS and resolution when
changing quality; do not translate “high quality” into 4K60. FPS accepts finite
numbers from 1 to 240, including 23.976 or 29.97. A draft can use Low quality;
choose a smaller resolution or fewer frames only when the user wants that tradeoff.
All four qualities work on Free at a 720p short side with the badge. Explicit
1080p/4K needs Pro and is refused before media preparation when unavailable.

`--start` and `--end` are seconds on the selected timeline, with an exclusive end.
They default to 0 and the timeline duration. Ranges must fit that timeline;
exports contain `ceil((end-start)*fps)` frames. Audio, clips and animation cuts
retain their source timing. No timeline rewrite is needed to export a range.

`--workers auto` is the default; `--runners` is a legacy alias. Manual choices:
1, 2, 4, 6, 8, 10, 12, 16, 20. `--mode quick|efficient` adjusts Auto; explicit
workers win. Capacity considers the scene, CPU, available RAM, other exports and
OS pressure; more workers are not always faster. `--software` forces software
encoding. Unknown, duplicate, missing or invalid option values are errors.

`--engine standard` is the default. `--engine single-pass` always uses one worker.
`--engine experimental-1` captures in parallel into one ordered final encoder.
All three are available on Windows, macOS and Linux, including production builds.
Single-pass and Experimental 1 are optional previews; no beta enrollment is needed.
Recoverable capture/encoder failures retry once through Standard with one software
worker after cleanup. Cancellation, missing media, storage and license failures
are not retried. The CLI prints all-black warnings and validates the finished
video before replacing any existing output. If it still fails, report the phase
and settings rather than changing the animation to hide a rendering failure.

`--timeline` renders a specific timeline of a multi-timeline project by id or display name (default: the active one); the default output name then becomes `<name>-<timelineId>-<preset>.mp4` so timelines never overwrite each other. Output defaults to `<project>/renders/<name>-<preset>.mp4`; `--out` overrides. Rendering triggers the same lazy v1→v2 migration as opening in the app (backup under `.migration-backup/`).

Rendering a `.coa` file: if this machine has a bound, unchanged working copy, that copy renders (including unsaved edits); otherwise the package temp-extracts, renders, cleans up, and output lands next to the `.coa` file.

```bash
coa render mcp-100s
coa render widget-voice --preset 4k60 --out ~/Desktop/widget.mp4
coa render ~/work/cool-project --preset 4k30 --mode quick
coa render ~/Downloads/storm-intro.coa --preset 4k30
coa render my-explainer --timeline shorts-cut --preset 1080p60
```

Headless Linux (CI, servers, no display): Electron needs a display server — wrap in `xvfb-run -a coa render …` (SwiftShader software GL takes over). Root additionally needs `--no-sandbox`.

Verify the output after every render:

```bash
ls -lh "$OUT" && ffprobe -v error -show_entries stream=width,height,r_frame_rate:format=duration "$OUT"
open "$OUT"      # macOS: play it   |   open -R "$OUT" reveals in Finder
```

## Packing and unpacking `.coa` files

```bash
coa pack <project> [--out path.coa]     # one-shot export → shareable .coa
coa unpack <file.coa> [--out dir]       # extract to a plain folder (must be empty)
```

`pack` mirrors the app's "Share .coa…" — the source stays a folder project, `renders/` + machine-local state are excluded. Default output `<Name>.coa` in the current dir. Linked projects refuse to pack. `unpack` only extracts — it does NOT import into the app (use Import → Open `.coa`… for that). Typical loop: unpack → edit → `coa pack` again.

## Troubleshooting


- **`coa` not found after menu install:** `ls -la /usr/local/bin/coa` should symlink into the app bundle (`/Applications/CoAnimator.app/Contents/Resources/coa` on macOS, `/opt/CoAnimator/resources/coa` on Linux deb). Re-run the menu install, or symlink manually (Step 1).
- **`timeline has N error(s)` → exit 1:** same Timeline Health validation as the desktop. Open the project in the app → **Timeline Health → Auto-fix safe** (or relink missing media), save, retry. Don't hand-edit timeline JSON to bypass — broken paths make ffmpeg fail mid-render with worse errors.
- **`FFmpeg not found`:** CoAnimator shells out to system ffmpeg. macOS `brew install ffmpeg`; Linux `sudo apt install ffmpeg`; Windows put `ffmpeg.exe`/`ffprobe.exe` on PATH or set `FFMPEG_PATH`/`FFPROBE_PATH`.
- **Any failure:** re-run with `DEBUG=1 coa render …` for the full stack trace. Exit code 0 = success, 1 = failure.
- **Long batches:** confirm with the user before looping every project — 20 projects × 3 min each is an hour. Loop `for p in <projects-dir>/*/`.
- **Partial renders ("t=10 to t=30"):** not a CLI feature. Trim in the app, or render full + `ffmpeg -ss 10 -to 30 -i in.mp4 -c copy out.mp4`.

## What's NOT in scope

- **Template installs, plugins, archive/delete/rename/import** — desktop launcher and Plugins page own these (`docs/project-management.md`, `docs/plugins.md`).
- **Per-frame override flags** — use the desktop Tweaks panel before export.
- **`coa component add`** — the component collection was removed; the command errors everywhere. Don't suggest it.
- **Desktop-only actions** — GIF conversion, publishing destinations and unsaved editor loop handles are not CLI render options. Use numeric `--start`/`--end` for a CLI range.

## Agent connections in the desktop app

For chat connections, sign-in, model selection, and disconnect behavior, read
`docs/agent-chat.md`. Chat connections and installed-agent sign-ins are
separate; never edit or copy their credential files. Manage them through the app.

CoAnimator's built-in agent supports pasted or attached PNG, JPEG, WebP and GIF images up to
5 MB each. Installed agents keep their own advertised image capabilities.
Use the composer’s Add context action for files; do not put image data into
CLI arguments. Keyboard and attachment behavior is documented in `docs/agent-chat.md`.

## Inspecting images and videos in chat

Read `docs/agent-media.md` before changing media attachment or inspection behavior.
Use local paths; never print base64 or upload a video to file hosting merely to
inspect it. With CoAnimator's built-in agent, `media_info` reads metadata and `view_media` returns
bounded image previews or selected video frames. Installed agents use their own
image-reading tools and FFmpeg/FFprobe with their normal approvals. Inspect
duration before choosing timestamps; sampled frames do not establish full motion
or audio correctness. Pasted bitmaps are saved locally and explicitly attached
external files receive scoped read access. See the doc for retention and limits.

For CoAnimator's built-in agent connection errors, see the temporary provider failures section
of `docs/agent-chat.md`. A transient 503 can be upstream availability; do not
request a new login solely for that status. Never replay a partially completed
agent turn automatically, since its tools may already have changed files.

## Optional setup and assistant-led studio guide

For connection onboarding, installation, media readiness or the project guide,
read `docs/onboarding.md`. Continue after choosing an assistant authorizes automatic
media preparation; failures show Media tools are missing. Only after the user
selects an assistant and its dynamic model in the recovery screen and presses
Start setup does the app send the predefined `coa-setup` request. Welcome shows a mini chat inside the modal
only after that request is recorded in the transcript; remembered models never send it
automatically. Next continues to templates without completing welcome early.
Normal permissions and questions remain in the embedded chat. Sent setup can continue
in the background, with an explicit chat-opening action; Settings recovery can use
the sidebar. Connecting an
assistant inline and skipping remain available. Install only missing media tools, retain normal permissions,
and verify before claiming success. Installations remain skippable/stoppable.
Encourage users to describe timeline changes in chat, preview the result and
refine it. The guide's examples open as drafts; never treat opening a guide or
choosing an example as permission to run an edit or install software.

Welcome/Coa telemetry is documented in `docs/onboarding.md`: welcome finishes only
after its preferences save, and Coa completion is a once-per-profile checklist
achievement rather than an export counter. Setup outcomes use usage analytics,
not crash alerts. Never manually submit local logs or skipped reports; disabling
usage sharing clears pending uploads and cancels outstanding requests.

### Chat session controls and questions

Chat checkpoints and restore are temporarily disabled for all sessions. Never modify `agent-chats/` or its stored checkpoints. Change receipts show only successful edit tools from the current session/turn; other windows' edits are not discovered through folder scans. Legacy scan-based lists are hidden on resume without deleting messages. A receipt is not a complete filesystem audit; an agent must report its edits for them to appear.

In CoAnimator chat, use the session's advertised commands and configuration
options. The gear shows Settings, Skills & commands and Tools; do not assume a
terminal-only `/mcp` or browser screen can render in chat. Explicit command
arguments remain agent prompts. Use the installed agent's terminal for settings
it does not expose to the chat client; that opens a separate session.

ACP form elicitation and Claude `AskUserQuestion` can ask for a user's choice
through a pending question card. Wait for its explicit accept/decline/cancel
response; answering does not grant tool permissions. Do not request secrets in
forms. See `docs/agent-chat.md` (Session settings, commands and questions) for
supported schemas, limits, cancellation and validation commands.

Model and argument suggestions come from the live agent/provider catalog and
advertised command hints. `/model` and `/effort` can show nested choices;
selecting one prepares the draft, while a typed value can be sent directly.
Do not maintain a static model allow-list or silently switch the user's
subscription/API connection when resolving a short model name. A manually
entered ID is still subject to the provider's validation.

## Export stall troubleshooting

Standard exports have bounded waits for frame capture, encoder input and finalization. Copy the export log and check `waiting`, `closed`, encoder and error text. Exit code 0 alone does not prove a valid video; empty or unreadable output must fail. The desktop and CLI retry encoder/capture stalls once with software encoding and one worker, reusing the chosen output file after stopping all failed workers. Cancel stops recovery; disk-space and permission errors do not retry. Copy log includes the failed operation and available memory; on macOS it also includes thermal state, so do not infer overheating from slow progress alone. Users can also reduce workers in Advanced export settings. Do not repeatedly restart a stuck export while its old workers are still running. See `docs/cli.md` for deadlines and `pnpm test:export` for regression checks.

### Agent connections and continuation

The connection categories are **Chat** (Claude, ChatGPT and Grok) and **CLI Agents** (installed coding agents). Chat describes the interface; the Claude account route runs native Claude Code, while ChatGPT, Grok and the Anthropic API route use CoAnimator's built-in agent.

- Claude setup offers Use installed Claude Code, Download and connect (the official native binary managed by CoAnimator), and Use an API key instead (Anthropic API, billed separately). The managed route runs Claude Code, not CoAnimator's model-only agent, and uses its own isolated sign-in/configuration. It downloads about 300 MB on explicit setup; the binary is not bundled in the app installer and requires no separate Node/npm installation. Do not copy global Claude credentials or settings into the managed connection.
- ChatGPT and Grok subscription connections remain available. The Anthropic API route uses CoAnimator's agent and validates model access before saving the key; that check does not establish credits or guarantee a generation request. OpenAI, Google and xAI API-key setup routes remain hidden. Preserve the selected connection on resume; do not switch between a subscription and API billing implicitly. Installed-agent catalogs and plugin API-key settings are separate.
- Google consumer Gemini CLI access ended June 18, 2026; Gemini models/enterprise API remain. New Google setup uses Antigravity's own `agy` terminal interface and official install guide, with Check again after installation. Legacy Gemini chats remain readable.
- Agents restores the last selected conversation for the project or Home scope. New chat inherits the current agent, account, model and exposed thinking effort; use the chooser to change them. History opens the folder conversation list and can browse nested folders separately. Resume retains the saved connection and model, subject to availability. Opening a chat does not send a prompt. CoAnimator's built-in agent continuation sends bounded recent context (up to 20 turns / 12 KB), not every previous tool output, after a process restart.
- Model lists are dynamic. Direct catalogs persist under the app's userData cache, reuse successful metadata for six hours, and allow a marked stale fallback for seven days. Installed agents still validate their cached display choices with the live handshake; never treat a cached choice as authorization or auto-select it.

- CLI auto-launch belongs to the main-process terminal, so reload/undock cannot discard it. Imported clip edits update the active stage clip once; playback ticks should not repeatedly republish an unchanged clip. See `docs/agent-chat.md` and `docs/editor.md`.

### Chat thinking settings

Agent settings uses the connected agent's advertised options. Direct subscription
chats expose thinking effort only when the model catalog supplies supported
levels; Model default sends no effort override. Claude's command-based effort
control prepares an editable `/effort` message and requires the user to send it.
Do not promise a level is active merely because it was added to the composer,
or recommend a hardcoded effort list for all models/providers.

### GIF sizing

For the separate Tools → Video to GIF window, width is a maximum after cropping;
small regions are never enlarged. A GIF may exceed the original MP4 size. Use
the smaller-GIF action, shorter trim, lower frame rate or width when appropriate,
and treat the middle-sample estimate as approximate. This is a desktop tool,
not a `coa render` GIF flag. See `docs/tools-library.md` for the sizing contract.

### Export engine previews

The desktop Export dialog offers Advanced → Engine → Single-pass (preview) on
Windows, macOS and Linux, including production installers. It uses one offscreen renderer and one
encoder; it still requires FFmpeg/FFprobe. Standard remains the default. The CLI uses `--engine single-pass` under the
same quality and license controls. If the
preview reports a paint/seek timeout, try Standard; if the hardware encoder
fails, the app retries with software. Test on a disposable project copy and
check duration, soundtrack, embedded video and crop at the selected resolution.

### Conversation entry and preferences

- Empty project/root agent panels and the + menu show exact-folder conversations first, with search and a primary New chat action. Starting new explicitly chooses an agent/model; resuming focuses an existing tab when possible.
- ACP model and effort selections are stored per conversation and validated against live choices on resume. Permission settings are not replayed. Older records without the optional index model still open without migration.
- Claude keeps its native transcripts. Small `agent-chats/preferences` records retain acknowledged model selections and successfully submitted advertised effort choices per folder/session; resume uses local CLI flags. Never rewrite global CLI settings to restore a conversation.

- OpenCode is temporarily hidden from agent pickers, while saved conversations and its driver remain supported. Command-line sections in agent pickers/setup are collapsed by default and expand using the chevron button.

### Export frame readiness and automatic resources

All export engines share a bounded frame-readiness implementation. `seekAndPause` /
`seek` plus committed `getTime` remains supported. Timeline-driven video elements
should stamp `data-kc-desired-time`; optional `data-kc-source-fps` describes their
source frame rate. An optional async `__kcControl.renderFrame(t, { fps })` may
explicitly await decoding and texture readiness. Do not resolve that hook while
a requested image is still loading. Read `docs/timeline.md` before changing the
player contract. Timeouts fail rather than accepting missing decoded data.

For comparison on Mac or Windows, use Export → Advanced → Engine. Auto is the
worker default; an explicit worker count is a deliberate override. Copy log
records stage timings and captured pixels. A short requested-size encoder probe
is a capability check, not a thermal stress test. The automatic software fallback
is scoped to the failing job. See `docs/cli.md` for the isolated real-engine check.

Oversized Retina frames are bounded to the output size before encoding.
Single-pass painting pauses between requested frames while FFmpeg consumes the
input; painting remains active during video/texture preparation. Keep these
behaviors in both the regression checks and real Mac engine comparisons.

Free exports remain limited to a 720-pixel short side in portrait, landscape and square, with the existing badge. The desktop rechecks the license before Save; main enforces it on encoder startup and finalization. Do not bypass these guards when changing export settings.

### Experimental 1: parallel capture, one final encode

All desktop builds offer **Advanced → Export engine → Experimental 1**. This is
a separate preview from Single-pass; Standard remains the default. The CLI uses
`--engine experimental-1` on Windows, macOS and Linux, including production.
Experimental 1 captures browser-encoded JPEG frames with a bounded ordered queue,
then runs one final FFmpeg encode with the same audio/video layers and watermark.
Worker overrides are supported. Keep FPS, quality, resolution and ranges unchanged
when comparing engines; output pixels and frame count are validated before saving.

All engines share the animation-time resolver and video readiness checks. Seeking
an adjacent animation cut selects the new segment's `trimIn` on the exact cut
frame, including backward/repeated source ranges. Do not shift clip start times
or add a frame to compensate. Standalone end-frame and nearest-edge gap holds
are unchanged; the fix requires no project/timeline migration. Seeking
embedded videos includes a one-microsecond boundary correction so fractional
60 FPS timestamps do not select the preceding decoded frame. Repeated held
frames reuse that corrected time. Scenes with `renderFrame` still own their
explicit decode/texture-readiness contract.

Capture/encoder failures in Experimental 1 retry once through Standard with one
software worker, after draining the failed job. Cancel prevents recovery. Final
outputs are saved atomically only after validation; an existing destination
survives a failed or cancelled export. Timeline and project files are not changed.
Use `pnpm test:export` for queue, recovery and timeline regression checks and
`pnpm test:export:electron` for isolated real Chromium/FFmpeg frame-order, video,
audio, overlay, portrait, cancellation and license checks. Windows GPU-driver
validation still requires a Windows machine; macOS tests are not a substitute.

Linux uses offscreen Chromium surface capture in Standard/CLI, Single-pass and
Experimental 1, including software recovery. Experimental 1 checks its first
frame before opening the remaining workers or starting its encoder. Keep the
readiness check, direct compositor-surface copy, transport crop and ordered queue
together; never reuse an old frame to recover a capture timeout. Offscreen paths
use `capturePage` after readiness, retaining cancellation and the eight-second
capture deadline. Do not replace that copy with an `invalidate()` / `paint`
listener: Electron may return a cached bitmap and repeat the previous frame.
Shared video composition evaluates visibility against the synchronized output
frame; do not restore generic overlay `enable`, which older FFmpeg can evaluate
using a different-rate background input and flash it at an animation cut. Keep
clip timestamps and layer order unchanged. Auto caps worker memory using
Linux MemAvailable or Windows available RAM (with headroom), while preserving
manual overrides. Raw Linux free pages exclude caches and do not prove pressure.
Copy log now records the capture backend, active worker phases and available RAM;
`frames=0` with only an FFmpeg banner means no frames were received, not proof
of an encoder crash or overheating. See `docs/cli.md` for the isolated Linux-path
test; actual Ubuntu/Wayland driver verification still requires that platform.
