Skip to content

tooling: automated TouchDesigner sweep (driver .toe + harness)#131

Closed
jcelerier wants to merge 3 commits into
mainfrom
tooling/td-sweep-automation
Closed

tooling: automated TouchDesigner sweep (driver .toe + harness)#131
jcelerier wants to merge 3 commits into
mainfrom
tooling/td-sweep-automation

Conversation

@jcelerier

Copy link
Copy Markdown
Member

Automates running every avendish object through TouchDesigner to catch instantiation/cook crashes — the one backend without a headless mode.

How

TouchDesigner.exe <file>.toe runs the project's Execute-DAT callbacks, so:

  • td_sweep_driver.py — paste into an Execute DAT (enable Frame Start) and Save As a driver .toe once. It waits a few frames (until all Custom OPs have registered), runs the generated sweep runner, writes the report, and quits. Runner/report locations come from env vars set by the harness.

  • run_td_sweep.py — the unattended harness:

    1. stages the built *_td.dll into the .toe's Plugins/ (then cleans up),
    2. generates td_run_all.py from the dump JSON (gen_tester_patches.py --backend td-runner) — instantiates and cooks every operator, recording errors()/warnings(),
    3. launches the driver .toe,
    4. auto-dismisses the modal "Plugin Load Error" dialog (BM_CLICK the OK button) that otherwise blocks startup (e.g. two installed plugins claiming the same opType),
    5. waits for td_test_report.json and prints N/total ok + each failure.

    An operator that crashes TD on instantiation/cook yields no report / an early exit — flagged.

  • README.md — the one-time GUI setup + usage.

Why the manual .toe step

TouchDesigner's binary .toe can't be reliably synthesized by hand — a toecollapse'd Execute DAT's callbacks don't fire (missing node metadata / TD falls back to the default project). Creating the driver .toe once in the GUI (≈2 min) is the reliable path; everything after is unattended.

Registration-only smoke (does a plugin load without crashing TD) needs no .toe — drop the DLLs in Documents/Derivative/Plugins/ and start TD. Verified separately: 199 avendish plugins load with no crash.

Windows only (TouchDesigner + Win32 dialog handling).

🤖 Generated with Claude Code

jcelerier and others added 3 commits July 1, 2026 08:36
TouchDesigner has no headless mode, but `TouchDesigner.exe <file>.toe` runs the
project's Execute-DAT callbacks. Adds:

- td_sweep_driver.py: paste into an Execute DAT (Frame Start on), Save As a
  driver .toe once. Waits a few frames (so Custom OPs have registered), runs the
  generated sweep runner, writes the report, quits. Runner/report paths come
  from env vars.
- run_td_sweep.py: the unattended harness -- stages *_td.dll into the .toe's
  Plugins/, generates td_run_all.py from the dump JSON (instantiate + cook every
  operator), launches the driver .toe, auto-dismisses the modal "Plugin Load
  Error" dialog (BM_CLICK the OK button) that otherwise blocks startup, waits for
  td_test_report.json, and prints a pass/crash summary. An operator that crashes
  TD on instantiation yields no report / an early exit, which is flagged.
- README.md: the one-time GUI setup + usage.

The .toe itself must be created once in the GUI: TouchDesigner's binary project
format can't be reliably synthesized by hand (a toecollapse'd Execute DAT's
callbacks don't fire). After that the sweep is fully unattended.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011s7huWR2wFsLFiMJPjx1z2
…sume)

Getting the sweep to actually instantiate operators surfaced several TD facts:
- opType is sanitize_td_name(C_NAME) (the binding passes @AVND_C_NAME@), not the
  display name -- fixed the type derivation in gen_tester_patches.
- TD's create() needs the OP's Python *class* (e.g. AvndadditionCHOP = opType +
  family), not the opType string; and a real COMP parent (root '/' won't hold
  leaf ops). The runner now resolves the class (trying families) via eval and
  creates inside a baseCOMP. It's fully inlined -- it's exec()'d in an Execute
  DAT callback frame, where nested defs / comprehensions can't see its locals.
- TD loads Custom OP DLLs only from the top level of Documents/Derivative/
  Plugins (not a .toe-adjacent Plugins/, not subfolders). The harness stages
  there, deduped by opType (a build emits Foo_CHOP_AUDIO + Foo_CHOP_MESSAGE,
  both the same opType), and removes exactly what it staged (retrying, as TD
  holds the handles briefly after kill).
- Crash resilience: the runner flushes the report after each op and drops a
  breadcrumb (td_current.txt); if an op hard-crashes TD, the harness records it
  and relaunches, and the runner resumes past everyone already tested.

First full run (110 objects): 61 ok, 45 "Not enough sources" (filters needing an
input -- benign), 3 class-not-found, and 1 that crashes TouchDesigner
(avnd_vb_fourses_tilde).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011s7huWR2wFsLFiMJPjx1z2
…rtifacts

Commit the driver .toe (a single Execute DAT that runs AVND_TD_RUNNER on
frame start and quits) so the one-time GUI step is optional -- run_td_sweep.py
works against it directly. Ignore the per-run outputs (generated runner, report,
breadcrumb, staged Plugins/).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011s7huWR2wFsLFiMJPjx1z2
@jcelerier

Copy link
Copy Markdown
Member Author

Superseded by #137, which combines all outstanding work into a single PR.

@jcelerier jcelerier closed this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant