Skip to main content

create-operator

MUST READ before creating or moving any operator (create_op, copy_op, set_op_position, or execute_python builds). Required verification, canonical positioning recipe, and error-checking steps.

소스 정보

저장소
dylanroscover/Embody
최근 소스 활동
2026년 9월 26일 06:32
감지된 SKILL.md 언어
영어
스타
191
포크
10

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
create-operator
description
MUST READ before creating or moving any operator (create_op, copy_op, set_op_position, or execute_python builds). Required verification, canonical positioning recipe, and error-checking steps.
<!-- Generated by Embody/Envoy - Do not remove this comment - sha:8f0ca97bbdf8c00f --> # Create Operator Workflow Follow these steps every time you create operators via MCP: 1. **Choose the correct parent network -- ASSOCIATE with where the user already works.** Inconsistent placement is the #1 scoping bug (`/` one run, `/project1` the next). The fix is to anchor on the user's own structure, which is stable across runs -- not on a transient pane: - **NEVER** create under `/local` or `/local/*` -- volatile storage, not saved with the `.toe`. - **If the user named a target**, use it. - **Default home = the container that holds the `Embody` COMP.** `execute_python` with `result = op.Embody.parent().path`. Embody in `/project1` -> build in `/project1`; Embody at the root `/` -> build at `/`. It returns the SAME home every run -- this is the consistency mechanism, and it is the level the user chose by placing Embody there. - **Override only for deliberate navigation.** If the user has actively opened a specific content network to work in, build there instead (`ui.panes.current.owner.path`) -- UNLESS that pane is sitting at the bare root `/` (a non-deliberate default, e.g. right after the project opens), in which case ignore it and use the Embody home above. Never treat bare `/` as a content home. - **Discover, never guess.** Confirm the real container names with `query_network` on `/` -- never hardcode `/project1` (it may be renamed, and there may be more than one). - **Keep one task's COMPs together** under the SAME parent, grouped in one container / annotation. Don't split related COMPs across levels. 2. **Discover the target network**: `query_network` on the target parent to confirm it exists and see existing operators 3. **Scan existing layout**: Use `get_network_layout` on the parent COMP. Note each operator's `nodeX`, `nodeY`, `nodeWidth`, and `nodeHeight` - operators vary in size (100-300+ units wide) 4. **Plan positions BEFORE creating**: Batch-compute grid-aligned positions for ALL operators you intend to create. Signal flow is left-to-right: inputs on the left, outputs on the right. Supporting operators (DATs feeding a TOP, CHOPs feeding parameters) go to the left of or below the operator they feed. Snap all coordinates to the 200-unit grid. See the Positioning Rules section below. 5. **Create each operator**: `create_op` with the desired type and name 6. **Position each operator**: `set_op_position` to place it at the pre-computed grid position. Auto-placement is NOT acceptable - it produces messy, unreadable networks. You MUST explicitly position every operator you create. Moving a host via `set_op_position` carries its docked companions along (re-hugged below the new spot, reported as `docks_moved`), so position the host FIRST and any deliberately-placed dock after. 7. **Docked companions (callback/shader/info DATs)**: `create_op`, `copy_op`, and `set_op_position` auto-hug every docked op in a tight row ~30 units below its host (`docks_placed` / `docks_moved` in the result) - do not re-plan grid slots for them. Ops created inside `execute_python` get NO such placement at create time (Envoy only auto-hugs badly scattered docks after the call, with a `LAYOUT WARNING`); if you create dock-spawning ops (GLSL TOP/MAT, execute DATs, OSC in/out) in a script, place their docks yourself per the Docked companion layout formula below. 8. **Connect**: `connect_ops` to wire inputs/outputs. Wires must flow left-to-right (positive X). If a wire would go backward, the downstream op is misplaced - reposition it. 9. **Set OP-reference parameters with relative paths**: If the operator has parameters referencing other operators (Camera, Geometry, Lights, TOP, CHOP, etc.), use sibling names (`cam`) or relative paths (`../shared/lut`) - NEVER absolute paths (`/project1/scene/cam`). See `parameters.md` section OP-Reference Parameter Values. 10. **Verify layout**: Call `get_network_layout` again. Confirm no overlaps, grid alignment is intact, signal flows left-to-right, and every entry carrying `dockedTo` sits in a tight row just below its named host (docked ops are the one exception to 200-grid spacing - they hug). 11. **Verify errors**: `get_op_errors` with `recurse=true` to check for errors and warnings. Fix all errors before considering the task complete 12. **Visual verification**: For any renderable result (a TOP chain or a render), capture the output TOP with `capture_top` and confirm it actually renders (not black) and matches intent; for a 3D render, confirm a camera, a light, and geometry display/render flags are present. For anything but a trivial op, load the `/visual-aesthetics` skill to judge composition/value/color/contrast. ## Operator Type Preferences - **Prefer POPs over SOPs** for geometry and particle work. POPs (Point Operators) are GPU-accelerated and significantly more performant than SOPs (Surface operators). Only use SOPs when POP equivalents don't exist or when CPU-side geometry manipulation is specifically required. ## Geometry COMP: delete the default torus A freshly created `geometryCOMP` ships with a default `torus1` SOP inside, **render flag ON**. The moment you add your OWN geometry to that COMP -- a SOP chain, a POP chain terminating in a `nullPOP`, or imported geometry -- **delete `torus1`** (or turn OFF its render flag). It is the RENDER flag (purple) that selects a SOP for the Render TOP, so leaving it on draws BOTH: your geometry AND a phantom torus. It is easy to miss: adding your own SOP auto-clears the torus's DISPLAY flag (blue, exclusive per viewer) so the SOP / geometry viewer looks correct, while its RENDER flag (non-exclusive) stays on and keeps drawing it in the render output. Applies to EVERY geometryCOMP you populate, not just POP builds. (TDXN import already strips these auto-created defaults, so this bites only live `create_op` builds -- which is why it shows up in some projects and not others.) ## Positioning Rules (canonical layout recipe) - **Every operator MUST be explicitly positioned** on the 200-unit grid. Do NOT rely on auto-placement. - **Spacing is `size + gap`, BOTH axes, ALWAYS -- never a fixed step.** This is the #1 overlap bug: stepping by a constant (e.g. `nodeY - 100`, `nodeX + 300`) while the tiles are bigger than the step (a 134-tall node stepped by 100 overlaps by 34). The step MUST be computed from the ACTUAL node extent on that axis plus a gap, then snapped up to the grid: - **Horizontal**: `next_x = prev_nodeX + prev_nodeWidth + gap` (gap >= 200), rounded up to the next 200 multiple. - **Vertical**: `next_y = prev_nodeY - (prev_nodeHeight + gap)`, rounded so the step clears the tile. For a stack of same-size tiles: `step = ceil((maxNodeHeight + gap) / 200) * 200` and place tile `i` at `y = -i * step`. A 134-tall stack -> `step = 200` (66 gap); a 260-tall stack -> `step = 400`. Bigger nodes => bigger spacing, mechanically. - **The grid is the SNAP target, not the step size** -- when a node is taller/wider than 200, the step is 400/600/... (the next multiple that clears `size + gap`), NEVER a flat 200. **400 units vertical minimum** between parallel chains or annotation groups (widen when tiles are tall). Y increases upward; new rows go downward. - **Use actual dimensions, not assumptions.** COMPs (~160+ wide) and panel widgets (often 130-160 wide x 130+ tall) are much bigger than a bare TOP (~120 sq) -- a step that clears one overlaps the other. - **Signal flow is left-to-right; wires must flow forward (positive X) -- never a backward "S" wire.** Every operator must have a higher `nodeX` than each operator feeding it. The classic mistake: placing a source at the SAME `nodeX` as its destination (directly above or below) -- the source's output then sits right of the dest's input and the wire loops back. Place each source so its right edge (`nodeX + nodeWidth`) is left of the destination's `nodeX`. If a wire bends backward, move the downstream op right or the source left. - **Branches split vertically**, each continuing left-to-right; minimize crossings. **Same row = same stage** in a chain (same X); **same column = same function** across parallel chains (same Y). - **New operators extend a group to the RIGHT** (`rightmost_nodeX + rightmost_nodeWidth + 200`, snapped); a parallel chain goes DOWN (lowest Y in group - 400). Don't fill a gap mid-row of a finished chain. - **Batch-compute ALL positions** before placing anything; **after placing**, `get_network_layout` to verify. ### Related operators stay close Operators that reference each other belong near each other, even with no wire or dock: a **MAT** beside the **Geometry COMP** it shades; a **camera** and **light(s)** near their **Render TOP**; a CHOP/DAT that drives parameters by reference near the op it drives; any COMP named in an `op()` call, expression, or material slot near the op that references it. Rule of thumb: if op A names op B, a reader should see both without scrolling. ### Panel-COMP widgets stack too Buttons/fields/containers inside an `align`-driven panel (Container/Button laid out `verttb`/`horizlr`) render by their `align` + `order` pars, so their `nodeX`/`nodeY` are cosmetic to the UI -- BUT they still get network tiles, and a `verttb` container full of 134-tall buttons stepped by 100 overlaps in the editor exactly like data-flow ops do. Apply the same `size + gap` stepping to panel-widget tiles (stack by panel `order`); it never affects the rendered panel. This is the one layout class the `LAYOUT WARNING` lint does NOT police -- it is on you to tidy. ### Docked companion layout formula Hosts that dock: execute/callback DATs (`chopExecuteDAT`, `datExecuteDAT`, `panelExecuteDAT`, `parameterExecuteDAT`, `executeDAT`); input DATs (`keyboardinDAT`, `mouseinDAT`, `oscinDAT`/`oscoutDAT`); and GLSL ops -- a `glslTOP`/`glslmultiTOP` docks a pixel DAT, a compute DAT, AND an info DAT (the `multi` variant also docks a vertex DAT); a `glslMAT` docks a vertex DAT, a pixel DAT, AND an info DAT (no compute DAT); a `glslPOP` docks a compute DAT and an info DAT. Given host bottom-left (`sx`,`sy`), size (`sw`,`sh`), and `N` docks (use max `dw`,`dh` across docks). Docks HUG the host -- the one deliberate exception to 200-grid spacing: - Row Y: `row_y = sy - dh - 30` -- a tight ~30-unit gap below the host's bottom edge, NOT a full grid step. - Slot step: `step = dw + 20` -- one compact cluster under the host. - Center the row: dock `i` at `nodeX = (sx + sw/2) + (i - (N-1)/2) * step - dw/2`, filling per this table; overflow stacks into a second tight row (`row_y - dh - 30`) rather than widening: | N | Pattern | |---|---| | 1 | `[C]` | | 2 | `[C, R]` | | 3 | `[L, C, R]` | | 4 | `[L, C, R, R2]` | | 5 | `[L2, L, C, R, R2]` | If another op occupies a dock's slot, **move that other op** -- docked ops take priority, are never threaded around obstacles, never overlap, never stranded. For `execute_python` builds: query `[d.path for d in op('PATH').docked]`, reposition EVERY dock per the formula, then `get_network_layout` to confirm. A `glslTOP` built inside `execute_python` is the classic trap. ### Annotations and complexity - Every operator inside exactly one annotation; no orphans; annotations never overlap (>= 400 units between edges); expand the annotation after every addition. Title names the FUNCTION, not the implementation ("Audio Mixing", not "CHOP chain 2"). - **MUST invoke `/manage-annotations` before `create_annotation` or `set_annotation`** -- `nodeX`/`nodeY` is the bottom-left corner. - **4-5 annotation groups** in one network: consider baseCOMPs (containerCOMPs for UI). **15-20 operators** in one group: consider encapsulating into a COMP that replaces the group, moving the annotation title to the COMP name/label. ### Anti-patterns - Leaving `execute_python`-created ops at (0, 0) / overlapping -- Envoy emits a `LAYOUT WARNING`, but position + verify regardless. - Fixed offsets like `nodeX + 300` without `nodeWidth` -- the #1 cause of overlaps. - TD's `COMP.layout()` -- overlapping, unreadable results. - Creating operators without updating the enclosing annotation; `set_op_position` into an unverified slot (it has no overlap detection). - Leaving docked callback/info DATs at their auto-spawn position after a script build. ## Key Reminders - NEVER skip positioning - auto-placement produces unreadable networks - NEVER place an operator on top of another operator - always scan first - Never rely on `layout()` for production networks - New operators go near related operators, not at origin - Docked companions hug their host (~30 units below, never a full grid step away). The MCP tools enforce this; after any `execute_python` build, check every `dockedTo` entry in `get_network_layout` yourself - For current network location: `execute_python` with `result = ui.panes.current.owner.path` - Placing a COMP: anchor on where the user put Embody (`op.Embody.parent().path`) for a consistent home; override only when the user has deliberately navigated into a content network (never bare `/`); discover, never guess, `/project1`; keep one task's COMPs in one home - New `geometryCOMP`: delete its default `torus1` the moment you add your own geometry, or it renders a phantom torus behind your scene
GitHub에서 보기