M13.15: qa project lifecycle; Collect Files makes its folder; agents cookbook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-03 12:56:41 -04:00
co-authored by Claude Opus 5.5
parent 77ab92fb7d
commit 12235b5dc5
5 changed files with 176 additions and 2 deletions
+35 -1
View File
@@ -55,9 +55,10 @@ The server speaks JSON-RPC 2.0 over stdio, one message per line, and supports MC
| `get_property {layer, path, comp?, time?}` | Value at a time, keyframes and expression. |
| `set_property {layer, path, value?, time?, expression?, comp?}` | Sets a static value. With `time` it sets a keyframe; with `expression` it sets an expression. |
| `add_keyframe {layer, path, time+value \| keys:[...], interpolation?}` | Adds keys, then optionally applies linear/bezier/hold/easyEase. |
| `render_frame {comp?, time?, max_side?, path?, inline?}` | Returns a PNG image of a frame. |
| `render_frame {comp?, time?, max_side?, path?, inline?, transparent?}` | Returns a PNG image of a frame; `transparent: true` keeps the alpha (as an RGB + Alpha render writes it) instead of compositing over the comp background. |
| `open_project {path \| demo \| new}` / `save_project {path?}` | Open and save files. |
| `undo {steps?}` / `redo {steps?}` | History. |
| `batch {steps: [{command, params}], label?, atomic?}` | Several commands in one call and one undo step (`engine.batch`); `"$N.key"` in a param is step N's result (`"$1.layer"`). A failing step rolls the batch back and names the step. |
| *(bridge)* `screenshot {panel?, id?}`, `ui_inspect`, `ui_elements {prefix?}`, `ui_click`, `ui_drag`, `ui_key`, `ui_type`, `ui_set`, `control {method, params}` | Look at and operate the live window. |
Layers are referenced by id (from `get_comp`), `"#n"` (1-based index from the top) or name. Comps are
@@ -78,6 +79,39 @@ instead of being ignored.
4. `execute_command {"command":"effect.apply","params":{"layer":2,"effect":"Gaussian Blur"}}`, then `set_property {"layer":2,"path":"effects/#1/blurriness","value":8}`
5. `render_frame {"time":1.0}` lets you look at the result, and `save_project {"path":"intro.ecproj"}` saves it.
### Cookbook (from end-to-end QA)
The scenarios in `crates/automation/tests/qa/` and `apps/effectcraft-cli/tests/qa_template.rs`
build real projects through these interfaces; they are worked examples of everything below.
* **Name things when you create them**: `layer.newText`, `layer.newShape`, `layer.newSolid`,
`layer.newNull`, `layer.newCamera` and `layer.newLight` take `name`, so later calls can say
`"layer": "Title"`. A reference that matches no layer is an error, never a silent no-op.
* **Use the paths replies give you**: `effect.apply` returns `{effects: [uid], paths:
["effects/#2"]}`; `layer.addShapeItem {kind, group?: uid|path}` returns `{uid, path}` (e.g.
`contents/group/contents/gfill`), ready for `set_property`. `@uid` works as a whole path.
* **One call, one undo step**: `batch {"steps":[{"command":"layer.newShape","params":{"kind":"ellipse","name":"Ring"}},{"command":"layer.addShapeItem","params":{"layer":"$1.layer","kind":"trim"}},{"command":"prop.addKey","params":{"layer":"$1.layer","path":"transform/opacity","time":0,"value":0}}]}`
(a string param that is exactly `"$N"` or `"$N.key"` is replaced; `$2.path` is the trim's path).
* **Expressions**: `set_property`/`get_property` replies carry `evaluated` (what renders) next to
`value` (the keyframed value) and `expressionError` when the expression fails.
* **Easing with Keyframe Velocity**: select keys by path, `keys.select {"keys":[{"layer":"Ring","path":"contents/trim/end","time":0}]}`,
then `keys.velocity {"outSpeed":0,"outInfluence":33.33}`.
* **Gradients**: `set_property {"path":"contents/gfill/colors","value":["#0080ff","#ffff00"]}` or
`{"colors":[[0,"#0080ff"],[1,[1,1,0]]],"opacities":[[0,1],[1,0.5]]}`.
* **Image sequences** import at 30 fps (Settings ▸ Import); `file.interpretFootage {"items":[id],
"frameRate":12}` conforms them (the item and layers that ran to its end get the new length).
* **Masks**: `layer.addMask {layer, shape: rect|ellipse, rect}` first, then `layer.mask.set
{field: feather|expansion|opacity, value}`.
* **Renders**: `renderQueue.add {comp, format: h264|prores|webm|png…, channels: rgba,
proresProfile: 4444, output}` then `renderQueue.render {"wait": true}`. Check a render by
importing it (`file.import`, `file.newCompFromSelection`) and `render_frame {transparent: true}`
for its alpha. (WebM VP9 alpha is written, but read back opaque for now.)
* **Essential Graphics**: controls can be addressed by name (`essential.set {"layer":"#1",
"control":"Title","value":"John Smith"}`); a command that targets an explicit `comp` runs even
when the active comp would disable it (`essential.exportTemplate {"comp":"Lower Third", …}`).
* **History**: Levels of Undo defaults to 32 (`prefs.set {"key":"general.undoLevels","value":99}`);
`history` lists branches and `history {"goto": id}` jumps between them.
### Motion tracking
Trackers live on the tracked layer (`Motion Trackers ▸ Tracker n ▸ Track Point n`) and are driven