Manage Annotations Workflow
Creating Annotations
create_annotation(
parent_path='/path/to/network',
mode='annotate', # 'annotate' (title bar), 'comment', or 'networkbox'
title='My Group',
text='Description of this group',
x=..., y=..., width=..., height=...,
color=[r, g, b], # Optional, floats 0-1
opacity=0.5 # Optional
)
Always favor annotations over OP comments for documenting operators or groups.
Enclosing Operators
To create an annotation that encloses a group of operators:
- Get positions:
get_network_layout on the parent COMP (includes all operator positions and bounding box)
- Calculate bounding box: Find
min_x, max_x, min_y, max_y (max includes operator width/height: max_x = max(op_x + op_w), max_y = max(op_y + op_h))
- Add padding: 70 units on left/right/bottom, 170 units on top (title bar + body text)
- Set coordinates:
nodeX = min_x - 70
nodeY = min_y - 70 (BELOW operators, not above!)
nodeWidth = max_x - min_x + 140
nodeHeight = max_y - min_y + 240 (70 bottom + 170 top)
Coordinate Model
nodeX/nodeY = bottom-left corner, width/height extend rightward and upward
- Title bar renders at the top of the rectangle
- Common mistake: Setting
nodeY above the operators. nodeY must be BELOW (less than) the lowest operator's Y.
Querying
get_annotations - list all annotations in a COMP with properties and enclosed operators
get_enclosed_ops - get operators enclosed by an annotation, or annotations enclosing an operator
set_annotation - modify text, title, color, opacity, position, or size
Deleting Annotations
Delete via delete_op, never via raw .destroy() in execute_python.
delete_op resolves utility annotations, purges any tracking, and arms an
auto-save checkpoint that re-exports the parent TDXN COMP's .tdxn without
the annotation -- the deletion is durable. A raw .destroy() leaves the
stale annotations: entry in the parent's .tdxn on disk, and the next
reimport of that COMP (import_network, manager Reload, or cold open)
resurrects the annotation with its pre-delete text.
annotateCOMP Quirks
utility is True for every annotation -- TD UI-drawn ones are born
that way, create_annotation sets it, and TDXN import applies it on every
annotation it recreates. (A bare Python parent.create('annotateCOMP')
is utility=False -- set ann.utility = True immediately to match; a
non-utility annotation is an ordinary COMP subtree that enumeration
sweeps will walk into.)
utility=True hides the op from op(), parent.op(), AND
.children -- only findChildren(includeUtility=True) sees it, and a
deep findChildren does not even DESCEND into a utility annotate's
subtree unless includeUtility=True is passed (verified live, TD
2025.33070). Paths THROUGH a utility annotate to its interior ops still
resolve via op().
- Every Envoy op-path tool resolves utility annotations (
delete_op,
set_parameter, set_op_position, get_op, ... -- they share one
utility-aware resolver). For DISCOVERY, use get_annotations (always
sees them) or pass include_utility=True to
query_network/find_children -- with the default False, annotations
are invisible in those listings.
- Annotations are never externalized per-op -- they round-trip through
the parent TDXN COMP's semantic
annotations: section. externalize_op
refuses them, and tagging sweeps skip them and their internals (the
widget internals are TD-managed stock content cloned from TDAnnotate).
envoy_bot_* is reserved for Embot, the mascot Envoy stands on the
operator it is working on (the Embot parameter). The read tools hide
those parts and report how many as embot_hidden; Embody strips them
from every saved file and deletes loose ones on save. Never create,
move, edit or delete an annotation with that prefix -- and never name
one of yours that way, or it will be deleted as an artifact.
.type returns 'annotate' (not 'annotateCOMP')
findChildren(type=annotateCOMP) requires the class object, not the string
- Cannot be reliably renamed after creation (TD also ignores a name passed
at create time -- rename right after creating instead)
1---2name: manage-annotations3description: MUST READ before creating, modifying, or querying annotations. Contains parameter names, coordinate model, and API quirks.4---5<!-- Generated by Embody/Envoy - Do not remove this comment -->67# Manage Annotations Workflow89## Creating Annotations1011```12create_annotation(13 parent_path='/path/to/network',14 mode='annotate', # 'annotate' (title bar), 'comment', or 'networkbox'15 title='My Group',16 text='Description of this group',17 x=..., y=..., width=..., height=...,18 color=[r, g, b], # Optional, floats 0-119 opacity=0.5 # Optional20)21```2223**Always favor annotations over OP comments** for documenting operators or groups.2425## Enclosing Operators2627To create an annotation that encloses a group of operators:28291. **Get positions**: `get_network_layout` on the parent COMP (includes all operator positions and bounding box)302. **Calculate bounding box**: Find `min_x`, `max_x`, `min_y`, `max_y` (max includes operator width/height: `max_x = max(op_x + op_w)`, `max_y = max(op_y + op_h)`)313. **Add padding**: 70 units on left/right/bottom, **170 units on top** (title bar + body text)324. **Set coordinates**:33 - `nodeX = min_x - 70`34 - `nodeY = min_y - 70` (BELOW operators, not above!)35 - `nodeWidth = max_x - min_x + 140`36 - `nodeHeight = max_y - min_y + 240` (70 bottom + 170 top)3738## Coordinate Model3940- `nodeX`/`nodeY` = **bottom-left corner**, width/height extend rightward and upward41- Title bar renders at the **top** of the rectangle42- **Common mistake**: Setting `nodeY` above the operators. `nodeY` must be BELOW (less than) the lowest operator's Y.4344## Querying4546- `get_annotations` - list all annotations in a COMP with properties and enclosed operators47- `get_enclosed_ops` - get operators enclosed by an annotation, or annotations enclosing an operator48- `set_annotation` - modify text, title, color, opacity, position, or size4950## Deleting Annotations5152**Delete via `delete_op`, never via raw `.destroy()` in `execute_python`.**53`delete_op` resolves utility annotations, purges any tracking, and arms an54auto-save checkpoint that re-exports the parent TDXN COMP's `.tdxn` without55the annotation -- the deletion is durable. A raw `.destroy()` leaves the56stale `annotations:` entry in the parent's `.tdxn` on disk, and the next57reimport of that COMP (import_network, manager Reload, or cold open)58resurrects the annotation with its pre-delete text.5960## `annotateCOMP` Quirks6162- **`utility` is `True` for every annotation** -- TD UI-drawn ones are born63 that way, `create_annotation` sets it, and TDXN import applies it on every64 annotation it recreates. (A bare Python `parent.create('annotateCOMP')`65 is `utility=False` -- set `ann.utility = True` immediately to match; a66 non-utility annotation is an ordinary COMP subtree that enumeration67 sweeps will walk into.)68- **`utility=True` hides the op from `op()`, `parent.op()`, AND69 `.children`** -- only `findChildren(includeUtility=True)` sees it, and a70 deep `findChildren` does not even DESCEND into a utility annotate's71 subtree unless `includeUtility=True` is passed (verified live, TD72 2025.33070). Paths THROUGH a utility annotate to its interior ops still73 resolve via `op()`.74- **Every Envoy op-path tool resolves utility annotations** (`delete_op`,75 `set_parameter`, `set_op_position`, `get_op`, ... -- they share one76 utility-aware resolver). For DISCOVERY, use `get_annotations` (always77 sees them) or pass `include_utility=True` to78 `query_network`/`find_children` -- with the default `False`, annotations79 are invisible in those listings.80- **Annotations are never externalized per-op** -- they round-trip through81 the parent TDXN COMP's semantic `annotations:` section. `externalize_op`82 refuses them, and tagging sweeps skip them and their internals (the83 widget internals are TD-managed stock content cloned from TDAnnotate).84- **`envoy_bot_*` is reserved for Embot**, the mascot Envoy stands on the85 operator it is working on (the `Embot` parameter). The read tools hide86 those parts and report how many as `embot_hidden`; Embody strips them87 from every saved file and deletes loose ones on save. Never create,88 move, edit or delete an annotation with that prefix -- and never name89 one of yours that way, or it will be deleted as an artifact.90- `.type` returns `'annotate'` (not `'annotateCOMP'`)91- `findChildren(type=annotateCOMP)` requires the class object, not the string92- Cannot be reliably renamed after creation (TD also ignores a name passed93 at create time -- rename right after creating instead)