<!-- Generated by Embody/Envoy — do not edit manually -->

# Embody + Envoy — AI Instructions

This project uses **[Embody](https://github.com/dylanroscover/Embody)** (TouchDesigner externalization) and **Envoy** (MCP server for AI coding tools).

- Embody externalizes TouchDesigner operators to version-controlled files (`.py`, `.tox`, `.tdn`, `.json`, etc.)
- Envoy provides MCP tools for AI assistants to inspect and modify the live TD network
- Use the `Aiclient` parameter on the Embody COMP to regenerate these files for your AI tool

> **Note:** For Claude Code users, a `CLAUDE.md` with modular `.claude/rules/` and `.claude/skills/` is also generated. This `AGENTS.md` is the universal fallback read by Cursor, GitHub Copilot, Windsurf, Gemini, and others.

---

## Critical Rules

1. **Prefer `.tdn` files for reading TDN-externalized COMPs** — `.tdn` files are JSON on disk with complete network structure (operators, parameters, connections, positions, flags, DAT content, annotations). Reading them directly is faster than MCP round-trips. Check `externalizations.tsv` (strategy column) to identify TDN-strategy COMPs. To edit: modify the `.tdn` file on disk, then call `import_network` via MCP with the COMP path, the parsed JSON, and `clear_first=True` to reload it in TD. Use MCP when you need live runtime state (evaluated expressions, cook errors) or for non-TDN operators.
2. **Use Envoy MCP tools for live TD state and non-TDN operators** — never say "I can't access that binary file." For operators not externalized as TDN, use MCP tools to inspect and modify them.
3. **Do NOT assume network paths** — use `query_network` on `/` to discover the actual root structure.
4. **Default to the current network** — use `execute_python` with `result = ui.panes.current.owner.path` to find the active pane.
5. **Never edit `externalizations.tsv` directly** — managed exclusively by Embody's tracking system.
6. **Always use forward slashes** in file paths.
7. **Check for errors and warnings after creating operators** — `get_op_errors` with `recurse=true` immediately after creation.
8. **Thread boundary**: MCP server worker thread must never import TD modules. All TD access goes through `_execute_in_td()`.

---

## TD Python Rules

- **Always use `.eval()`** to get a parameter's current runtime value. `.val` only returns the constant-mode value.
- **Setting `.val` silently switches mode to CONSTANT** — destroys any active expression.
- **Toggle parameters** use `0`/`1` (not `"True"`/`"False"`).
- **Use `opex()`** when the operator must exist — raises immediately with a clear error. `op()` returns `None` silently.
- **Never call `op()`, `parent()`, or access TD objects at module level** — defer to methods.
- **NEVER access TD objects from a worker thread** — all TD operations must go through main-thread hooks.
- **`fetch()` searches UP the parent hierarchy** by default — pass `search=False` for local-only lookup.
- **`tdu.Dependency`**: Assign to `.val` (`dep.val = 5`), NOT the object itself. Call `.modified()` after mutating contents.

## Network Layout Rules

- **200-unit grid**: All positions snap to multiples of 200. Never place at arbitrary coordinates.
- **300 units horizontal** between connected operators in a chain.
- **Left to right** signal flow: inputs on the left, outputs on the right.
- **NEVER place an operator on top of another** — scan with `query_network` and `get_op_position` before placing.
- **Every logical group gets an annotation** — use `create_annotation` around each cluster of related operators.
- **Annotations must enclose their operators**: `nodeX`/`nodeY` is the bottom-left corner — `nodeY` must be below (less than) the lowest operator's Y.
- **TD Y-axis increases upward** — new rows go downward (decreasing Y).

## MCP Server Safety Rules

- Envoy must bind to `127.0.0.1`, NEVER `0.0.0.0`.
- `_execute_in_td()` times out at 30 seconds.
- The MCP server runs as a `standalone=True` TDTask (long-lived, outside the worker pool).
- Uses `threading.Event` + `Queue` rather than locks because TD's cook cycle is frame-based.

---

## Project Structure

```
Embody/
├── AGENTS.md                              # This file — universal AI instructions
├── CLAUDE.md                              # Claude Code specific instructions (if using Claude)
├── dev/
│   ├── embody/
│   │   ├── externalizations.tsv          # Tracking table (NEVER edit directly)
│   │   └── Embody/                       # Main extension source
│   │       ├── EmbodyExt.py              # Core externalization engine
│   │       ├── EnvoyExt.py               # MCP server extension
│   │       └── TDNExt.py                 # TDN network format export/import
└── release/
    └── Embody-v*.tox                     # Latest release build
```

## Extension Referencing

```python
# Promoted methods (uppercase) — called directly on the component:
op.Embody.Update()
op.Embody.Save()

# Non-promoted (lowercase) — through ext:
op.Embody.ext.Embody.getExternalizedOps()
op.Embody.ext.Envoy.Start()
```

**NEVER cache extension references in variables** — always call inline.
