agentleFS
dylanroscoverdylanroscover/Embody/.claude/skills/multi-instance/SKILL.md

multi-instance

Skill213 starsChanged 2 months agoMITView on GitHub

Workflow for running multiple TD instances with Envoy, switching between them, and understanding the instance registry.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If occupied by this TD process's old server, force-close it and reclaim

    line 63
  2. Very specific:

    If occupied by another process, scan base+1 through base+9

    line 64
  3. Very specific:

    Call switch_instance with no parameters to see all registered instances and their status:

    line 81
  4. Very specific:

    When you open the same .toe file in multiple TD instances, Envoy auto-suffixes the registry key to avoid collisions.

    line 148

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Multi-Instance Workflow

Envoy supports multiple TouchDesigner instances running simultaneously in the same git repo. Each instance gets its own port, and the bridge can switch between them on demand.

Architecture

Claude Code  <-->  STDIO Bridge  <-->  Envoy (TD instance A, port 9870)
                        |
                        +---switch--->  Envoy (TD instance B, port 9871)
  • One bridge process per SESSION (spawned by each AI client's MCP client)
  • Per-session pinning - each bridge pins to an instance NAME and re-resolves its port from the registry every tick, so a pinned instance restarting on a new port still self-heals, and a save that renames its .toe is followed (same TD process). The registry active field only seeds NEW bridges without a pin.
  • A vanished pin fails closed - if the pinned instance leaves the registry (closed, crashed), the session has no target: calls return envoy.instance.unavailable and nothing is forwarded - never to another instance - until it re-registers (a call that names another instance with instance= still goes there). Recover with launch_td (reopens the pinned .toe) or switch_instance.
  • Per-call addressing - every Envoy tool accepts an optional instance argument; the bridge routes that one call to the named instance and leaves the pin alone (unknown or unreachable names fail only that call with error_code envoy.instance.unknown / envoy.instance.unreachable). Prefer it over switching when you only need one look at another instance.
  • Switching is instant and session-local - switch_instance re-pins THIS session's bridge in-memory; peers are untouched unless you pass all_sessions=True (writes the registry default and bumps active_epoch, which moves every session)
  • Registration never re-routes running sessions - a new instance takes the active default slot only when it is vacant or names a dead instance

Instance Registry (.embody/envoy.json)

Each Envoy instance registers itself in .embody/envoy.json at the git root on startup:

{
  "active": "Embody-5.257",
  "td_executable": "/Applications/TouchDesigner.app",
  "instances": {
    "Embody-5.257": {
      "toe_path": "dev/Embody-5.257.toe",
      "port": 9870,
      "td_pid": 12345
    },
    "MySecondProject": {
      "toe_path": "MySecondProject.toe",
      "port": 9871,
      "td_pid": 67890
    }
  }
}
FieldPurpose
activeDefault instance for NEWLY-spawned bridges (each bridge then pins per-session; only an active_epoch bump re-targets pinned bridges)
td_executablePath to TD app (used by launch_td)
instances.<name>.toe_pathRelative path to the .toe file
instances.<name>.portEnvoy HTTP port for this instance
instances.<name>.td_pidOS process ID of the TD process

Instances are added on Envoy startup and removed on graceful shutdown (onDestroyTD).

Port Allocation

Each instance picks a port from a 10-port range starting at the configured Envoyport parameter (default: 9870):

  1. Try the base port first
  2. If occupied by this TD process's old server, force-close it and reclaim
  3. If occupied by another process, scan base+1 through base+9
  4. First available port wins; if all 10 are taken, startup fails

This means up to 10 simultaneous instances per base port.

Workflow: Running Multiple Instances

1. Open the first .toe file

Launch TD normally or via launch_td. Envoy starts on its configured port and registers in .embody/envoy.json.

2. Open additional .toe files

Open them in separate TD instances (File > Open or double-click). Each Embody/Envoy auto-starts and claims the next available port.

3. List instances

Call switch_instance with no parameters to see all registered instances and their status:

switch_instance()

Returns each instance's name, port, PID, reachability (PID alive + port responding), and whether it's the active target.

4. Switch to a different instance

switch_instance(instance="MySecondProject")

The bridge immediately redirects to the target instance's port. All subsequent MCP calls go to that TD process.

5. Switch back

switch_instance(instance="Embody-5.257")

Reachability Checks

An instance is reachable only when:

  • Its registered PID is alive (kill -0 / process table check)
  • Its registered port responds to a TCP connect

Both conditions must be true. A dead PID with an open port means another instance reused that port - the entry is stale.

get_td_status includes the full instance registry with reachability in its response.

Stale Instance Cleanup

Instances are deregistered on graceful TD shutdown. If TD crashes:

  • The PID becomes dead, so the instance shows as unreachable
  • The port may be freed, allowing a new instance to claim it
  • Stale entries remain in .embody/envoy.json but are filtered by reachability checks
  • Re-launching TD with the same .toe overwrites the stale entry

Closing Instances

Preferred: Close TD instances via Envoy by calling execute_python with project.quit(). This prompts the user to save unsaved changes, then triggers onDestroyTD for clean deregistration and port release. Works reliably across platforms.

To close a specific instance, switch_instance to it first, then send the quit command:

switch_instance(instance="MySecondProject")   # target the instance
execute_python(code="project.quit()")         # user gets save prompt in TD

Never use project.quit(force=True) unless the user has explicitly asked - it skips the save dialog and risks losing unsaved work.

Avoid: osascript -e 'quit app "TouchDesigner"' and similar OS-level approaches are unreliable - they may not target the correct instance and don't guarantee clean Envoy shutdown.

Common Scenarios

ScenarioAction
Test code in two .toe files side by sideOpen both, switch_instance between them
Test code in a secondary .toeOpen it, switch to it, perform work, switch back
Check if a second TD is still runningswitch_instance() (list mode) or get_td_status
One instance crashed, want the otherswitch_instance to the surviving instance
Close a specific instanceswitch_instance to it, then execute_python with project.quit()

Same-Project Instances

When you open the same .toe file in multiple TD instances, Envoy auto-suffixes the registry key to avoid collisions. The first instance registers as MyProject, the second as MyProject-2, etc. Stale entries (dead PIDs) are automatically reclaimed.

Limitations

  • The bridge connects to one instance at a time - no parallel MCP calls to multiple instances
  • Maximum 10 instances per base port range
  • .embody/envoy.json is per git root - instances in different repos have separate registries
  • launch_td and restart_td act on this session's pinned instance and its .toe (unpinned: the registry default) - pass project_path to open another file
  • Opening the same .toe file in multiple instances auto-suffixes keys (MyProject-2, -3, etc.)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.