dylanroscover / merge-divergent-tox
Merge or compare two divergent TouchDesigner .tox/.toe binaries that git cannot three-way-merge. Load when a merge/rebase conflicts on a .tox or .toe, when asked to merge, port, reconcile, or diff two versions of a TD component, or when comparing a component across branches, machines, or TD builds.
<!-- Generated by Embody/Envoy - Do not remove this comment - sha:13d8c4fc5bd64bed -->
Merging Divergent .tox / .toe Binaries
Git cannot three-way-merge a .tox. It reports a conflict and offers you "ours or theirs", a choice that silently discards one side's work. This skill turns the binary into a reviewable text diff. Phases 0 to 2 need no TouchDesigner at all and carry no risk; in practice they often answer the whole question, so do not skip them to get to the interesting part. The commands for Phases 1 to 5 live in references/procedure.md in this skill's folder (get_guidance(topic='merge-divergent-tox/procedure') without a skills folder).
The rule that governs everything here
This is a THREE-way merge, not a two-way one. Both sides changed the file from a common ancestor. If you compare only ours against theirs, a difference tells you nothing about who moved: an operator present in theirs and absent in ours may be their addition or our deletion, and those demand opposite actions. Always diff each side against the BASE.
Phase 0: triage (no tools)
- Enumerate every conflicting binary, not just the ones you were asked about.
git diff --name-only --diff-filter=Ufor the active conflicts,git diff --stat <base>..<theirs> -- '*.tox' '*.toe'for everything that diverged. The file nobody mentioned is the one that breaks later. - Find the true per-file content base.
git merge-baseis often wrong: history rewrites and re-committed blobs move it. Verify per file; if the history was rewritten, compare commit SUBJECTS rather than SHAs to find which commits are genuinely absent. - Answer the product questions BEFORE opening TouchDesigner. If one side deleted a subsystem the other side extended, that is a product decision, not a merge decision. Resolving it can collapse the hardest files to "take ours" and end the job. Ask the human. This is the highest-leverage step in the skill and it costs nothing.
The phases and their gates
| Phase | What | The gate |
|---|---|---|
| 1 Extract | git cat-file blob all three sides into ours/, theirs/, base/ from Bash, never PowerShell | Size equals the on-disk blob and the header reads 0000 0001 3130, or ABORT: a filter mangled the bytes |
| 2 Inventory | toeexpand.exe (works on .tox) each side to an ASCII tree, comm the operator lists against BASE, grep .parm/.cparm for values | Judge by whether the .dir appeared, never by the exit code (it returns 1 on success). Most merges end here |
| 3 Sandbox | Only if Phase 2 left real work. A purpose-made empty .toe outside every live git root, its own git init, its own port; create the host COMP, allowCooking = False, THEN loadTox, in one call | Never in a production project: extensions run at instantiation, before any hook, and Safe Mode does not stop that |
| 4 Export and diff | export_network(embed_all=True, include_dat_content=True) both sides | embed_all is mandatory; export one side twice first to learn the format-noise floor |
| 5 Apply | Classify every difference against BASE (theirs-added, ours-added, both-changed, convergent), apply into ours, re-export, re-diff until only intended differences remain | COMP.save(), reload, diff once more: the round-trip gate |
What this technique CANNOT see
A clean diff is not proof of equivalence. Invisible to it:
| Blind spot | Consequence |
|---|---|
| Embedded VFS media (movies, images, fonts) | Never appears at all. Sanity-check file size against what the structure explains |
| Export-mode parameters (CHOP exports) | Not stored in TDXN; a CHOP-export difference is completely invisible |
par.enable / enableExpr / password | Conditional-enable rules silently lost |
| Fully-default, unwired operators | Dropped from TDXN export entirely; Phase 2 covers them, another reason not to skip it |
| Locked TOP/CHOP/SOP/POP frozen data | Arrives locked and empty |
| Parameters equal to a creation default that changed between TD builds | Two exports can match while the live networks differ. Always merge in the NEWER build, applying the older side's delta onto the newer |
| Derived DAT content under cooking-off | Empty, not deleted |
| Panel visual position | Captured only as ordinary parameters; eyeball it |
Budget an eyeball pass in the TD UI over both loaded COMPs before accepting any merge of a UI or networking component.
What Embody already does for you
diff_tdxncompares TDXN networks and is the right instrument once both sides are loaded or exported; pair it withexport_network(embed_all=True).- Externalizing to TDXN in the first place is the real fix. A component tracked as
.tdxnis YAML on disk: git three-way-merges it like any other text file and this skill becomes unnecessary for it. If you are merging the same.toxby hand twice, externalize it as TDXN instead.
The judgement, which side wins and whether a subsystem is being retired, stays human.