Changelog
Перевод английской документации от 29.09·English
All notable changes to this project are documented here.
The format follows Keep a Changelog, and the project follows Semantic Versioning.
Two things get an entry here without fail, because a reader cannot discover either from the code they are running:
- A change to the bridge protocol version. The bridge component runs inside TouchDesigner and outlives an upgrade of the host package, so a protocol change is the one kind of change that asks the reader to run
td-atlas reload. - Anything that changes what the connector may touch in the user's project.
#[0.3.0] - 2026-09-29
Measured against TouchDesigner 2025.32460 on macOS. The Windows code paths run in CI; TouchDesigner on Windows is unverified. See docs/compatibility.md.
#Changed
- The bridge speaks protocol 9, and protocol 9 is now also the minimum the host accepts. Run
td-atlas reloadonce in every project that holds the bridge, and restart the MCP server (in practice, the client that launched it).health_probe,script_errors_recheckandrecordjoined the method table andhealth_samplegainedcounts_only. td_healthtells a static operator from a branch nothing pulls. The[ERROR] not-cookingfinding fired on anything that did not cook: a Ramp TOP with fixed settings, a Text DAT, a material — three agent sessions read it as breakage. Each quiet operator is now asked to cook, twice, 0.2 s apart. One that computes something new at the second ask isnot-pulled(warning); one that computes nothing isstatic(note). Never-cooked operators, outputs and whatever falls past a half-second budget are not asked and are named as such. The probe cooks what it asks, as a viewer would. The first sample also no longer cooks anything: reading a Level TOP's pixel format cooked it and the operator above it, which then read as running.
- A refused connection says whether TouchDesigner is running. With a TouchDesigner process up and the port silent, the error (and
doctor) says the open project has not loaded the bridge and quotes the bootstrap line to paste;td-atlas installis named only when the staged files are missing or differ from the package. With no process, it says that. The MCP hint for the first case is new:bridge_not_loaded. On Windows, where the process is not read, the message is unchanged. - An
AttributeErroronNonepoints at the path.op('/wrong').parused to get the hint for a misspelt parameter; it now getsnone_attribute: nothing exists at that path, check it withtd_network. TouchDesigner's owntdAttributeError(a misspelt parameter) now gets the parameter-name hint instead of "no mapped recovery".
#Added
td_healthnames the operators that carry state between frames. Every operator with a reset pulse (Trigger, Speed, Count, Lag, Feedback, Trail...) is listed in the notehistory: a frame reached by setting the timeline's frame is not the frame playback gives, and a take needs them reset.health_samplecarrieshistoryper operator.td_recordrecords a take with its sound in one call. Three agent sessions did it by hand, about ten calls a take, and lost takes to each step. It resets every operator with aresetpulseunder the scope, pauses at the first frame, turns realTime off, builds a fresh Movie File Out (ProRes, PCM sound fromaudio) with a keep-alive, starts it a step later, plays, ends when Info CHOP'sactive_recordsfalls to 0, and puts realTime, play mode and the nodes back. It answers with frames written of frames asked and the sound samples; H.264/H.265 on Non-Commercial and an existing file are refused up front. New bridge methodrecord, part of the bump above;td_timeline_statusandtd_timeline_cancelfollow it.td_healthcatches a uniform array longer than its CHOP. The shader compiles, nothing warns, and the elements past the data are undefined (measured: NaN, then 1.0);glsl-array-lengthnames the array, its declared length and the sample count.health_samplecarriesarrayMismatchfor GLSL operators.- The schema carries what a parameter does that its name does not say. Measured on 2025.32460 and printed under the parameter by
td_operator_schemaandtd-atlas op: Ramp TOPradialis the angle andcircularthe distance from the centre; Level TOP runs its Pre page (brightness1, gamma1, contrast) before its Range page; a Text TOP's position is an offset from the pointalignx/alignypick; a Wireframe MAT's line width and a camera's fog have no effect on it on macOS. td_buildandtd_set_paramssay what happened besides the ask. After each create: the DATs TouchDesigner docked beside the new node (a GLSL TOP's_pixel/_info/_compute, a Script CHOP's_callbacks), each marked used or a leftover; a new Geometry COMP'storus1with its flags; a new SOP or POP inside one whose render flag is off, and who has it. After each write: an OP parameter that reads backNone, whatever path was written.op_createreplies carryalsoCreated,unresolved,render,displayandrenderFlagOnfor this;par_setreplies carryunresolved.- Later blocks of a parameter sequence can be set.
const1nameon a Constant CHOP orarray1nameon a GLSL TOP was refused by the name check, and on a fresh operator was no parameter at all; two agents moved that work intotd_exec. The check now judges a later block's member as the first block's, the bridge grows the sequence before writing it, and a number on the sequence's own parameter ("const": 4) sets the block count — TouchDesigner ignores that write on its own. td_search_operatorsandtd-atlas searchknow a few algorithm names. "marching cubes", "isosurface", "metaball", "SDF" and "dilate/erode" appear on no operator page, so a search for them ranked Cube Map TOP first and never reached Polygonize POP. A query naming one now lists the operators that do it first, with atechniqueline saying how; the dilate/erode line says no operator does it and a GLSL TOP is the way.
#Fixed
- A
td_execpast 30 s is no longer called an asleep TouchDesigner. A script that sleeps or waits uses no CPU, and the timeout diagnosis read that as App Nap and sent the agent to bring the window forward. The script was running, and it finishes: a 35 s script wrote all its files after the call gave up. The refusal now says so (exec_timeout). - A frame taken right after editing a synced file runs the new code. TouchDesigner re-reads a file-synced Text DAT on a poll (measured 0.7 s), so an exec or render straight after an edit on disk ran the old code.
td_exec,td_render,td_timeline_runandtd_recordreload every synced Text DAT whose file differs from its text first, and say so (resyncedin the reply). - A traceback that is no longer raised stops reading as live. TouchDesigner keeps a traceback's text after the code is fixed, and
td_healthreported it as an error on every check. It now clears each kept traceback and looks again 0.2 s later: back —script-errors(error); not back while its operator cooked —script-errors-stale(note), left cleared; not back and nothing cooked —script-errors(warning), text put back. New bridge methodscript_errors_recheck, part of the bump above. - A
td_exectraceback starts at the real error. The statement run sat inside the handler of the failed eval attempt, so every error was chained to a false "SyntaxError: invalid syntax" at line 1. And aresultin Cyrillic (or any non-ASCII text) prints as itself intd_execandtd-atlas exec, not as\uescapes.
- A project whose file name is not ASCII opens and writes back.
toeexpandandtoecollapseread their argument's UTF-8 bytes as Latin-1, so readingкубы.toxfailed with "Error opening file: кубы.tox" and a pointer totd-atlas doctor. Both tools now work on a copy under an ASCII name; the file itself and the name in every answer stay as they were.
#[0.2.0] - 2026-09-24
Measured against TouchDesigner 2025.32460 on macOS. The Windows code paths run in CI; TouchDesigner on Windows is unverified. See docs/compatibility.md.
#Changed
- The bridge speaks protocol 8, and protocol 8 is now also the minimum the host accepts. Run
td-atlas reloadonce in every project that holds the bridge, and restart the MCP server, which in practice means restarting the client that launched it. Host and bridge are checked against each other only at connect, by the numbers each loaded when it started: a server started before the upgrade refuses the reloaded bridge as newer than it expects, and the new server refuses every bridge that was not reloaded. A bridge laid down by an earlier build is refused at connect with that fix in the message. Five methods joined the table (timeline_run,timeline_status,timeline_cancel,timeline_profile,trace), and six replies gained fields an older bridge does not send:ping(absFrame,tickand atimelineblock), the error reply ofexec(stdout,stderr,result),par_set(before),health_sample(cookAbsFrame,cookFrame,feedback,negativeFloat,clockReads,clockReadsCount,clockScanwith itsscriptscount), andnetworkandop_info(vieweron a TOP). - The call journal records what a changing call changed, not only that it ran. Parameter writes, builds, creations, deletions, wiring, flags, palette loads, extensions, annotations, saved components, the code
td_execran and the walk a timeline job was sent now leave their values on the journal line in~/.td-atlas, so "put it back the way it was yesterday" can be answered from the log. A parameter write keeps the value it replaced, which the bridge reads in the same request before writing, andtd_logprints it astx = 0.5 (was 0.2). Every line is clipped (code and DAT text at 4 KB, 32 KB a line), the token is scrubbed as before, and other secrets are hidden by pattern: a quoted value given to a name such aspassword,secret,tokenorapi_key, and keys that announce themselves (sk-…,ghp_…,xoxb-…,AKIA…), become[redacted]. The file cap rises from 1 MiB to 16 MiB.td_logandtd-atlas logprint the change under each call. Calls that only read are logged as before, and the frame pollssettle_framesmakes are not logged at all. td_statusandtd-atlas statusname the timeline's own frame, whether it is playing, its start and end, its playback range and rate, apart fromabsTime.frame(abs), which does not follow the timeline's frame and stands still while the root timeline is paused; waits counttick(op.TDResources.time.frame). The status used to printabsTime.frameunder the word "frame". A playback range narrower than the timeline is marked, since a recording stops atrangeEndwithout a word.- A bridge timeout reads the TouchDesigner process before blaming a script. One that sits idle, asleep, minimised or behind a dialog is reported as such, with the advice to bring it to the front. Every timeout used to say a long script was blocking the main thread. On Windows the old message stands.
- A script that raises in
td_execstill returns what it printed, and itsresult, ahead of the error.td-atlas execwrites them where they would have gone. The reply is still a failure. td_healthfinds five more quiet failures. A cook time is named by the frame it was measured on, so one left from long before the check is no longer blamed for the current frame rate. Feedback loops, whichcook(force=True)does not advance, are listed. So are bypassed gain operators (Level, Math, HSV Adjust), which pass their layer through at full strength rather than switching it off, and Level TOPs that can put negative floats into an Add. Reads ofabsTimeor an unseeded random generator, which keep a render from reproducing, are named with where they are; script text and parameter expressions share one time budget, and the finding says how much of each it read. A bypassed gain operator is listed once, under its own warning, and no longer also among the plain bypassed operators.td_networkmarks a TOP whose viewer flag is off, so an empty tile is not taken for a broken scene.td-atlas installprints the MCP line for every directory (-s user) beside the one for the current directory. It runs neither.td-atlas doctorsays how far over its ceiling the expansion cache is and that it is draining, instead of printing the count beside the limit as if the limit did not hold.
#Added
- Timeline jobs:
td_timeline_run,td_timeline_statusandtd_timeline_cancel. A job walks the timeline frame by frame inside TouchDesigner and saves a TOP's frames, for anything that depends on history (live audio, Feedback TOPs, trails), where atd_execloop hits the 30 s limit. It returns a job id at once. Without an output it only advances the timeline and holds it paused where it stopped. What it touches in the project: it pauses the timeline and gives the play mode back, and withtilesit crops the named Render TOPs and puts the crop back to 0..1 however the job ends. The files it writes stay. MCP only, declared inAGENTS.md. td_rendertakessave_to, which writes the PNG to that path and answers in text, andsettle_frames(--settle-frameson the CLI), which waits that many TouchDesigner frames first. A render right after an edit could show the frame before it. The frames are counted onop.TDResources.time.frame, which keeps counting while the timeline is paused;absTime.framestands still then, and a wait on it reported a drawing TouchDesigner as stalled.save_torefuses a file that already exists unlessoverwrite=True, like every other write td-atlas makes;render -oon the CLI overwrites as a shell redirect does.- The index records the tuple members a size menu adds (
amp2on a noisePOP, with the menu value that shows it), aftertd-atlas probe. The validator refuses a member the size in force does not show, and a size menu the probe cannot step is reported. td_trace: why the output is black. It walks up a TOP's inputs, reads each image's minimum, mean and maximum without forcing a cook (reading an image that is out of date still makes TouchDesigner cook that node once, which on a Feedback TOP is a step of the loop), and marks where the picture dropped, went negative, lost its alpha or turned NaN, with the cause the parameters show (bypass, opacity 0, a black level, an empty input, a Level TOP in float with no clamp and contrast above 1, inlow above 0 or outlow below 0). It stops at 40 operators and half a second of reading. MCP only.td_timeline_profile: who spends the frame. A job in the same slot astd_timeline_runwalks real timeline frames and, on each, cooks every TOP, CHOP, SOP and POP under a path upstream first and times it with the GPU waited for — atd_execloop ofcook(force=True)shows about 0 ms for a TOP that costs 80, since the cook only queues GPU work.td_timeline_statusshows mean and maximum per operator, costliest first, and marks the ones that did not cook on their own. Forced cooks run side effects (scripts, file and network outputs) once more; the tool says so.td_snapshotends its reply with the next step of a before-and-after comparison of a branch, and says when a label it reused replaced an earlier snapshot. The skill gives the whole recipe: two snapshots of the branch,td_project_diffbetween them, and the roll-back.- The skill notes sequence parameters, the Execute DAT's callbacks, one-shot Movie File Out, deferred runs on a paused timeline, bypassed Level TOPs and the undoable route through
td_execfor an edittd_buildcannot express, next to the traps the newtd_healthfindings describe.
#Fixed
td_buildrefuses a step whose keys the bridge would ignore, and names the right one.op_connectwithinput_indexused to wire input 0.- A StrMenu takes free text; its entries are suggestions. A plain Menu stays closed.
- An operator-reference parameter written as
../nameis refused only when the path TouchDesigner will look it up at is missing, and the refusal gives the sibling and absolute spellings. td_set_paramschecks names and../operator references even withoutop_type: it asks the bridge for the node's type in the same call it already makes for references.- The test suite stays out of the real
~/.td-atlas. It had been appending a journal line there.
#[0.1.0] - 2026-09-15
The first release. Everything below is what it contains.
Measured against TouchDesigner 2025.32460 on macOS. The Windows code paths run in CI; TouchDesigner on Windows is unverified. See docs/compatibility.md.
#Added
scripts/publish.shgates a release on a clean working tree, a bundle built from the currentHEAD, a free version tag, and a green test run. Each gate runs before anything goes outward.README.mdcovers installation from a clone, troubleshooting, and what the connector may and may not change in a project.- A
livepytest marker.pytestruns the suite that needs no TouchDesigner;pytest -m liveruns the tests that drive a running instance. install.shis the whole path to a working install in one line. Runcurl -fsSL https://grigabyte.github.io/td-atlas/i | shon a machine that has git and a Python 3.11 or newer. It finds that interpreter by asking each one for itsversion_info, clones or fast-forwards the checkout, makes a virtualenv beside it, installs the package, builds the offline index and runstd-atlas install, then reprints the two lines to paste. Every command is printed before it runs. It writes in td-atlas's own two places, the directory you choose and~/.td-atlas. No system directory is touched, nothing is installed with sudo, and no shell startup file is edited. Whichever ofuvandpipdoes the install also fills its own package cache,~/.cache/uvor~/Library/Caches/pip, as it would for any package.uvis used when it is already onPATHand never installed, because uv's own installer edits the shell rc this script promises to leave alone. Run a second time it fast-forwards the existing checkout, reuses the virtualenv and leaves an existing index alone, since rebuilding it would drop the runtime pass onlytd-atlas probeagainst a live instance can put back.- The short install address serves the tracked script itself.
.github/workflows/pages.ymlrepublishesinstall.shunder the nameion every push that changes it, so the file served is the file in the repository. A stale copy under a friendlier address is worse than a long URL, because the person running it cannot see that it is stale. Theraw.githubusercontent.comURL stays in the README as the second line, for when Pages is not answering.
#Changed
- The bridge speaks protocol 7, and protocol 7 is now also the minimum the host accepts. A bridge component laid into a project by an older build is refused at connection time with the fix in the message. Such a build used to be accepted, and then answered
UnknownMethodto every newer call, or answered a different question in silence. Re-lay the component withtd-atlas reload, which is exempt from the check so that the one command able to replace an old handler can still reach one. What counts as a protocol change is now written down inAGENTS.mdand held bytests/test_protocol_fingerprint.py. The method table, each method's parameters and the reply shape the host reads are all the wire, and the number moves with any of them. - The Claude Code plugin moved from the repository root to
plugin/. Installing it brings the TouchDesigner skill and nothing else; previously it pulled in every tracked file in the repository. - Reading a
.toe/.toxunpacks it into a cache that now has a ceiling of 200 expansions and evicts by least recent use. Before this the cache only ever grew, on one machine to 2209 expansions and 4.9 GB.td-atlas doctorprints its size, andtd-atlas doctor --clear-cacheempties it. - Refusing to overwrite an existing output file moved out of the MCP tool and into the layer both surfaces share, so
td-atlas project writeis now covered by it too. It was not, andtoecollapsemoves whatever it finds in the way aside to<file>.bkp1. - Answers that had to be cut short now say so.
td_networkreportstruncated,childrenHiddenanddepthLimited;td_errorsandtd_healthreport how much of the network went unvisited. A partial read used to be indistinguishable from a complete one, including a "nothing wrong found" that had only looked at a corner of the network. td_errorstakes apathand walks the project by default. It used to sweep from/, where its 5000-node budget is spent on TouchDesigner's own/uiand/sysbefore the walk reaches the project. On an open session it answered "no operators are reporting errors" about a project it had never looked at. The reply now also names the subtree it walked, in every branch.pathis a new argument on an existing bridge method, and it is the second reason the protocol number is 7. A bridge staged before this change accepts the argument, ignores it and walks/anyway, and nothing in its reply distinguishes that from a correct answer. Such a bridge is refused at connect on its version.td_networksays how many direct children a component at the edge of the requested depth has. The walk stops there without recursing and without a marker, so a leaf and a component holding thousands of operators came back as the same line. On an open session/performand/uiread alike. The count was in the reply all along and only the printing was missing.td_healthreports its own blind spots. A script-error read that fails is a finding in the report. Its sampling interval is bounded, so a largeintervalcan no longer block the server process.- Network walks and the frame capture buffer are bounded (5000 nodes, 512 MiB). Every request runs on TouchDesigner's main thread, so an unbounded walk is a freeze of the application.
- The CLI and the MCP surface now return the same number of results by default.
td-atlas search --limitandtd-atlas project grep --limitmatchtd_search_operatorsandtd_project_grep.td-atlas searchgained--family,td-atlas opgained--include-hiddenandtd-atlas project grepgained--limit, none of which the terminal had. Every remaining difference between the two surfaces is written down inAGENTS.mdand held there by a test. README.mdcloses four holes a first reader fell into. An MCP-capable agent is named in the requirements, where the reader meets it; it used to surface six sections later as a bareclaude mcp addline. As an MCP server now carries seven sentences in the language a person actually uses beside the calls each turns into, so "you say, it does" rests on something. The skill's two reference files say at their head who the reader is, since thetd_names are calls an agent makes. And the one-line installer takes the top of Install, with the manual clone kept below as the same steps by hand. The paragraph that declared three addresses to be 404s until the first release is gone with the repository going public. Two of them now resolve, and the third, the release thatdist/server.jsonpoints at, is named where it appears.
#Removed
- Three bridge methods are gone.
perf,par_getandsavehad no caller on the host and no test covering them, andsavecalledproject.save(), a Save As that moves the artist's working file. Anything reaching the bridge directly (this package is not the only thing that can) loses them;health_sampleandop_infocarry what the first two returned. This removal is the first of the two reasonsPROTOCOL_VERSIONis 7. The method table is the wire, so which methods exist is part of the protocol even though nothing this package's own two halves exchange changed shape. Leaving the number at 5 would have let one version name two different method sets. docs/decisions.mdanddocs/publishing.mdare no longer in the repository. Both were written for a reader who does not exist. The register of decisions explains the shape of the code to the people who chose it, and the release gates are for whoever runsscripts/publish.sh, which is the owner alone. Those five gates are written out in that script's own header, where the README now sends a reader. Comments insrc/that cited the register by number carry the reason in a few words instead, so nothing in the code points at a document a reader cannot open.
#Fixed
.mcp.jsonis no longer tracked. It carried an absolute path to one machine's virtual environment.td_logandtd-atlas logno longer report "No calls recorded yet" when the journal is failing to be written; the reason is printed under the report. A write error used to be swallowed whole.- The bridge token is scrubbed from journal lines using the current token. The cache holding it never expired, so a token rotated by
td-atlas installwent into the log in clear while the previous one was removed.
[0.2.0]: https://github.com/grigabyte/td-atlas/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/grigabyte/td-atlas/releases/tag/v0.1.0